├── .gitattributes ├── .github └── workflows │ ├── build.yml │ └── doxygen-gh-pages.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Doxyfile ├── LICENSE ├── Readme.md ├── include ├── fft2d.hpp ├── fonts.h ├── imfilebrowser.h ├── ini.h ├── libnpy.hpp └── tinycolormap.hpp └── src ├── BoundedThreadPool.hpp ├── Camera.cpp ├── Camera.h ├── FileConnector.cpp ├── FileConnector.h ├── GuiUtils.cpp ├── GuiUtils.h ├── ImGuiINI.hpp ├── ImGuiImageWindow.cpp ├── ImGuiImageWindow.h ├── ProgressMonitor.cpp ├── ProgressMonitor.h ├── Ricom.cpp ├── Ricom.h ├── RunCLI.cpp ├── RunGUI.cpp ├── SdlImageWindow.cpp ├── SdlImageWindow.h ├── SocketConnector.cpp ├── SocketConnector.h ├── cameras ├── MerlinInterface.cpp ├── MerlinInterface.h ├── MerlinWrapper.cpp ├── TimepixInterface.cpp ├── TimepixInterface.h └── TimepixWrapper.cpp └── main.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=C++ 2 | *.hpp linguist-language=C++ 3 | *.cpp linguist-language=C++ -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | # University of Antwerp - All Rights Reserved. 3 | # You may use, distribute and modify 4 | # this code under the terms of the GPL3 license. 5 | # You should have received a copy of the GPL3 license with 6 | # this file. If not, please visit: 7 | # https://www.gnu.org/licenses/gpl-3.0.en.html 8 | 9 | # Authors: 10 | # Thomas Friedrich 11 | # Chu-Ping Yu 12 | 13 | name: Compile 14 | 15 | on: [push, pull_request, workflow_dispatch] 16 | 17 | jobs: 18 | build_win: 19 | runs-on: windows-latest 20 | steps: 21 | - uses: actions/checkout@v2 22 | with: 23 | submodules: true 24 | - uses: ilammy/msvc-dev-cmd@v1 25 | with: 26 | arch: amd64_x86 27 | 28 | - name: get sdl, fftw 29 | run: | 30 | curl.exe http://www.libsdl.org/release/SDL2-devel-2.0.16-VC.zip --output sdl.zip 31 | curl.exe http://www.libsdl.org/projects/SDL_image/release/SDL2_image-devel-2.0.5-VC.zip --output sdl_im.zip 32 | tar -xf sdl.zip 33 | tar -xf sdl_im.zip 34 | ren SDL2_image-2.0.5 SDL2_IM_VC 35 | ren SDL2-2.0.16 SDL2_VC 36 | move .\SDL2_IM_VC\include\* .\SDL2_VC\include\ 37 | move .\SDL2_IM_VC\lib\x64\* .\SDL2_VC\lib\x64 38 | move .\SDL2_IM_VC\lib\x86\* .\SDL2_VC\lib\x86 39 | move SDL2_VC C:\ 40 | del sdl.zip 41 | del sdl_im.zip 42 | mkdir FFTW 43 | cd FFTW 44 | curl.exe https://fftw.org/pub/fftw/fftw-3.3.5-dll64.zip --output fftw.zip 45 | tar -xf fftw.zip 46 | del fftw.zip 47 | lib /machine:x64 /def:libfftw3f-3.def 48 | cd .. 49 | move FFTW C:\ 50 | 51 | - name: Build with MSVC 52 | run: | 53 | mkdir build 54 | cd build 55 | cmake .. 56 | msbuild .\RICOM.vcxproj /p:configuration=Release 57 | - name: Archive production artifacts 58 | uses: actions/upload-artifact@v4 59 | with: 60 | name: Release_Win 61 | path: | 62 | build\Release 63 | 64 | build_Linux: 65 | runs-on: ubuntu-latest 66 | steps: 67 | - uses: actions/checkout@v2 68 | with: 69 | submodules: true 70 | - name: Get SDL2, fftw3 and gcc-11 71 | run: | 72 | sudo apt-get update 73 | sudo apt-get install libsdl2-dev libsdl2-image-dev libfftw3-dev gcc-11 g++-11 74 | - name: Build 75 | env: 76 | CC: gcc-11 77 | CXX: g++-11 78 | run: | 79 | cmake . -D CMAKE_BUILD_TYPE=Release 80 | make 81 | - name: Archive production artifacts 82 | uses: actions/upload-artifact@v4 83 | with: 84 | name: Release_Linux 85 | path: | 86 | RICOM 87 | -------------------------------------------------------------------------------- /.github/workflows/doxygen-gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: Build Documentation 2 | 3 | on: [push, pull_request, workflow_dispatch] 4 | 5 | jobs: 6 | deploy: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Install Graphviz 10 | run: sudo apt-get install graphviz -y 11 | shell: bash 12 | - uses: DenverCoder1/doxygen-github-pages-action@v1.1.0 13 | with: 14 | github_token: ${{ secrets.GITHUB_TOKEN }} 15 | branch: gh-pages 16 | folder: docs/html 17 | config_file: Doxyfile -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | intel_build 3 | data 4 | .vscode 5 | imgui.ini 6 | *.mib 7 | *.png 8 | *.out 9 | *.zip 10 | opengl32.dll 11 | ricom.log 12 | m_list.py 13 | RICOM 14 | html 15 | latex 16 | fake_merlin.py 17 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "imgui"] 2 | path = imgui 3 | url = git@github.com:ocornut/imgui.git 4 | [submodule "merlin_interface"] 5 | path = merlin_interface 6 | url = https://gitlab.com/tfriedrich/merlin_interface.git 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | # University of Antwerp - All Rights Reserved. 3 | # You may use, distribute and modify 4 | # this code under the terms of the GPL3 license. 5 | # You should have received a copy of the GPL3 license with 6 | # this file. If not, please visit: 7 | # https://www.gnu.org/licenses/gpl-3.0.en.html 8 | # 9 | # Authors: 10 | # Thomas Friedrich 11 | # Chu-Ping Yu 12 | # 13 | 14 | cmake_minimum_required(VERSION 3.16.0) 15 | 16 | project(RICOM LANGUAGES C CXX) 17 | set(CMAKE_CXX_STANDARD 17) 18 | 19 | if(NOT CMAKE_BUILD_TYPE) 20 | set(CMAKE_BUILD_TYPE Release) 21 | endif() 22 | 23 | MESSAGE(STATUS "Using ${CMAKE_CXX_COMPILER_ID} Compiler!") 24 | message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") 25 | 26 | if (UNIX) 27 | set(SDL2_INCLUDE_DIR /usr/include/SDL2) 28 | set(FFTW3_DIR /usr/include) 29 | set(CMAKE_CXX_FLAGS "-Wall -Wextra -pthread") 30 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 31 | set(CMAKE_CXX_FLAGS_RELEASE "-Ofast") 32 | # SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") 33 | # SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg") 34 | # SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pg") 35 | endif(UNIX) 36 | 37 | if (WIN32) 38 | set(SDL2_INCLUDE_DIR C:/SDL2_VC/include/) 39 | set(SDL2_LIB_DIR C:/SDL2_VC/lib/x64) 40 | set(FFTW3_DIR C:/FFTW) 41 | set(BUILD_DIR ${PROJECT_BINARY_DIR}/${CMAKE_BUILD_TYPE}) 42 | MESSAGE(STATUS ${PROJECT_BINARY_DIR}/${CMAKE_BUILD_TYPE}) 43 | link_directories(${SDL2_LIB_DIR}) 44 | link_directories(${FFTW3_DIR}) 45 | 46 | if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") 47 | set(CMAKE_CXX_FLAGS "-Wall -Wextra -pthread -std=c++17") 48 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 49 | set(CMAKE_CXX_FLAGS_RELEASE "-Ofast") 50 | endif() 51 | 52 | if (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") 53 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17") 54 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /nologo") 55 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GL") 56 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GA") 57 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2") 58 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MT") 59 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Qpar") 60 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /favor:INTEL64") 61 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2") 62 | endif() 63 | 64 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "IntelLLVM") 65 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Qstd:c++17 /Ox") 66 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Qopt-assume-no-loop-carried-dep=2") 67 | # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /QxKABYLAKE") 68 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2") 69 | set(MMD_LIB C:/Program\ Files\ \(x86\)/Intel/oneAPI/compiler/latest/windows/redist/intel64_win/compiler) 70 | set(BUILD_DIR ${PROJECT_BINARY_DIR}) 71 | file(COPY ${MMD_LIB}/libmmd.dll DESTINATION ${BUILD_DIR}) 72 | file(COPY ${MMD_LIB}/libmmdd.dll DESTINATION ${BUILD_DIR}) 73 | file(COPY ${MMD_LIB}/svml_dispmd.dll DESTINATION ${BUILD_DIR}) 74 | endif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "IntelLLVM") 75 | 76 | file(COPY ${SDL2_LIB_DIR}/SDL2.dll DESTINATION ${BUILD_DIR}) 77 | file(COPY ${SDL2_LIB_DIR}/SDL2_image.dll DESTINATION ${BUILD_DIR}) 78 | file(COPY ${FFTW3_DIR}/libfftw3f-3.dll DESTINATION ${BUILD_DIR}) 79 | file(COPY ${FFTW3_DIR}/libfftw3f-3.lib DESTINATION ${BUILD_DIR}) 80 | endif (WIN32) 81 | 82 | add_executable(RICOM src/main.cpp) 83 | 84 | target_sources( RICOM PRIVATE 85 | imgui/misc/cpp/imgui_stdlib.cpp 86 | imgui/imgui_draw.cpp 87 | imgui/imgui_tables.cpp 88 | imgui/imgui_widgets.cpp 89 | imgui/backends/imgui_impl_opengl3.cpp 90 | imgui/backends/imgui_impl_sdl.cpp 91 | imgui/imgui.cpp 92 | src/SocketConnector.cpp 93 | src/FileConnector.cpp 94 | src/ProgressMonitor.cpp 95 | src/Ricom.cpp 96 | src/cameras/TimepixInterface.cpp 97 | src/cameras/TimepixWrapper.cpp 98 | src/cameras/MerlinInterface.cpp 99 | src/cameras/MerlinWrapper.cpp 100 | src/Camera.cpp 101 | src/main.cpp 102 | src/GuiUtils.cpp 103 | src/SdlImageWindow.cpp 104 | src/ImGuiImageWindow.cpp 105 | src/RunCLI.cpp 106 | src/RunGUI.cpp 107 | ) 108 | 109 | target_include_directories( RICOM PUBLIC 110 | imgui 111 | imgui/backends 112 | imgui/misc/cpp 113 | ${SDL2_INCLUDE_DIR} 114 | ${FFTW3_DIR} 115 | include 116 | src 117 | src/cameras 118 | ) 119 | 120 | find_package(OpenGL REQUIRED) 121 | 122 | target_link_libraries(RICOM PUBLIC ${OPENGL_LIBRARIES}) 123 | if (WIN32) 124 | target_link_libraries(RICOM PUBLIC SDL2main SDL2 SDL2_image libfftw3f-3 ${CMAKE_DL_LIBS}) 125 | else () 126 | target_link_libraries(RICOM PUBLIC SDL2main SDL2 SDL2_image fftw3f ${CMAKE_DL_LIBS}) 127 | endif (WIN32) -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # riCOM 2 | [![Compile](https://github.com/ThFriedrich/riCOM_cpp/actions/workflows/build.yml/badge.svg)](https://github.com/ThFriedrich/riCOM_cpp/blob/master/.github/workflows/build.yml) 3 | [![Build Documentation](https://github.com/ThFriedrich/riCOM_cpp/actions/workflows/doxygen-gh-pages.yml/badge.svg)](https://github.com/ThFriedrich/riCOM_cpp/actions/workflows/doxygen-gh-pages.yml) 4 | [![License: GPL3](https://img.shields.io/badge/License-GPL3-brightgreen.svg)](https://opensource.org/licenses/GPL-3.0) 5 | 6 | This repository contains the C++ implementation of the riCOM (Real Time Centre Of Mass) algorithm for 4D Scanning electron microscopy as described in [this publication](https://www.cambridge.org/core/journals/microscopy-and-microanalysis/article/realtime-integration-center-of-mass-ricom-reconstruction-for-4d-stem/40AC3F51175C32763ADF355E58073355). At this point it is compatible with .mib files (Quantum Detectors Merlin) and .t3p (Timepix camera) data and can be run in live mode with the Merlin camera. 7 | 8 | ![gui](https://user-images.githubusercontent.com/47680554/211806138-87dd32db-e15d-406f-a55e-187495668741.png) 9 | 10 | ## Installation 11 | ### Binaries 12 | - Just download the precompiled executable for your system from [Releases](https://github.com/ThFriedrich/riCOM_cpp/releases) or for the latest development version the artefacts of the automated compilation run from the Repositories "Actions" Tab, for example [here](https://github.com/ThFriedrich/riCOM_cpp/actions/workflows/build.yml)! 13 | - The Windows version includes additional libraries, which need to be kept in the same folder as the executable. Linux requires SDL2 and FFTW3 libraries on your system (see below). 14 | - Alternatively build from source as outlined below. Make sure you clone the repository including submodules: ```git clone --recurse-submodules -j2 git@github.com:ThFriedrich/riCOM_cpp.git``` 15 | - The project uses features of C++ standard 17. You may need appropriate compilers and libraries. 16 | - Generally the performance/speed may not be ideal using precompiled binaries. For best results compile on the machine you want to run the software on, using the "native" option for the "ARCH" variable in the CMakeLists.txt file 17 | 18 | **Generally build instructions can be followed step by step from the [automated build setup](https://github.com/ThFriedrich/riCOM_cpp/blob/master/.github/workflows/build.yml) from command line** 19 | ### Build instructions Linux 20 | #### Dependencies 21 | - [g++/gcc](https://gcc.gnu.org/) compiler version >=9 22 | - ```sudo apt-get install gcc-9 g++-9``` 23 | - [make & cmake](https://cmake.org/) 24 | - [SDL2](http://www.libsdl.org) 25 | - ```sudo apt-get install libsdl2-dev libsdl2-image-dev``` 26 | - [FFTW3](https://fftw.org/) 27 | - ```sudo apt-get install libfftw3-dev``` 28 | 29 | #### Building from Command Line 30 | ```bash 31 | mkdir build 32 | cd build 33 | cmake -DCMAKE_BUILD_TYPE=Release .. 34 | cmake --build . 35 | ``` 36 | 37 | ### Build instructions Windows 38 | #### Dependencies 39 | - [Visual Studio Community](https://visualstudio.microsoft.com/vs/community/) with C++ Desktop Development Kit or msbuild tools 40 | - including the msvc compiler, cmake and msbuild 41 | - [SDL2](http://www.libsdl.org) 42 | - Download 'SDL2-devel-2.0.xx-VC.zip' (Visual C++ 32/64-bit) from http://www.libsdl.org/download-2.0.php#source and [SDL2-image](http://www.libsdl.org/projects/SDL_image/release/SDL2_image-devel-2.0.5-VC.zip) 43 | - Both zip folder share a common sub-structure. Extract contents of the zips to 'C:/SDL2_VC', merging the contents of both zips into the same folder structure. 44 | - [FFTW](https://fftw.org/) 45 | - Download and extract [FFTW3 library](https://fftw.org/pub/fftw/fftw-3.3.5-dll64.zip) 46 | - run ```lib /machine:x64 /def:libfftw3f-3.def``` 47 | 48 | #### Building from Command Line (**Use __Developer__ Powershell for VS**) 49 | ```bat 50 | mkdir build 51 | cd build 52 | cmake .. 53 | msbuild .\RICOM.vcxproj /p:configuration=Release 54 | ``` 55 | The executable RICOM.exe will be in the folder 'build\Release'. Make sure **the files 'SDL2.dll', 'SDL2_image.dll', 'libfftw3f-3.dll' and 'libfftw3f-3.lib' are in the same location as the executable**. When using oneAPI `icx` compiler from Intel, also the libraries: **the files 'svml_dispmd.dll', 'libmmdd.dll' and 'libmmd.dll'** must be in this directory. 56 | 57 | ## Running the program 58 | ### Running in Live mode with Quantum Detector MerlinEM camera 59 | To send instructions to the camera a python scipt is executed internally, which relies on the [Merlin Interface package](https://gitlab.com/tfriedrich/merlin_interface). It is a submodule of this repository, but it can also be manually downloaded. The file [merlin_interface.py](https://gitlab.com/tfriedrich/merlin_interface/-/blob/master/merlin_interface/merlin_interface.py) should be placed in the same directory as the RICOM executable. 60 | 61 | ### Running example files 62 | A set of compatible example datasets are provided in an open data repository on [Zenodo](https://zenodo.org/record/5572123#.YbHNzNso9hF). 63 | 64 | ### Command Line Interface 65 | The program can be run from command line without opening a GUI. The input file path and other parameters are being specified by additional parameters as listed [here](https://github.com/ThFriedrich/riCOM_cpp/blob/master/src/RunCLI.cpp). 66 | Example usage: 67 | ```bash 68 | ./RICOM -filename default1.mib -nx 64 -ny 64 69 | ``` 70 | This functionality was originally intended for dubugging purposes. It is therefore not fully developed yet, but may be useful for some users nevertheless. 71 | 72 | ## License 73 | This program is free software: you can redistribute it and/or modify 74 | it under the terms of the GNU General Public License as published by 75 | the Free Software Foundation, either version 3 of the License, or 76 | (at your option) any later version. 77 | 78 | This program is distributed in the hope that it will be useful, 79 | but WITHOUT ANY WARRANTY; without even the implied warranty of 80 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 81 | GNU General Public License for more details. 82 | 83 | **You may contact the [University of Antwerp](jo.verbeeck@uantwerpen.be) for alternative licensing options if 84 | necessary.** 85 | 86 | ## Cite 87 | If you use this software in your research please cite: 88 | ```bibtex 89 | @article{ 90 | ricom_2022, 91 | title={Real-Time Integration Center of Mass (riCOM) Reconstruction for 4D STEM}, 92 | DOI={10.1017/S1431927622000617}, 93 | journal={Microscopy and Microanalysis}, 94 | publisher={Cambridge University Press}, 95 | author={Yu, Chu-Ping and Friedrich, Thomas and Jannis, Daen and Van Aert, Sandra and Verbeeck, Johan}, 96 | year={2022}, 97 | pages={1–12}} 98 | ``` 99 | -------------------------------------------------------------------------------- /include/fft2d.hpp: -------------------------------------------------------------------------------- 1 | // Loosely Based on: https://github.com/lschw/fftw_cpp (GPL3 license) 2 | 3 | #ifndef __FFTW2D_CPP__HH__ 4 | #define __FFTW2D_CPP__HH__ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | /** 11 | * Class representing a 2D Fourier transform 12 | */ 13 | class FFT2D 14 | { 15 | public: 16 | const size_t N1; // Number of data points 1 17 | const size_t N2; // Number of data points 2 18 | const size_t N; // Total number of data points = N1*N2 19 | 20 | private: 21 | fftwf_plan plan_fw; 22 | fftwf_plan plan_bw; 23 | 24 | public: 25 | /** 26 | * Setup Fourier transform 27 | * @param N1 Number of datapoints in first dim. 28 | * @param N2 Number of datapoints in second dim. 29 | */ 30 | FFT2D(int N1, int N2) : N1(N1), N2(N2), N(N1 * N2) 31 | { 32 | plan_fw = fftwf_plan_dft_2d( 33 | N1, N2, 0, 0, FFTW_FORWARD, FFTW_ESTIMATE); 34 | plan_bw = fftwf_plan_dft_2d( 35 | N1, N2, 0, 0, FFTW_BACKWARD, FFTW_ESTIMATE); 36 | } 37 | /** 38 | * Clean up 39 | */ 40 | ~FFT2D() 41 | { 42 | fftwf_destroy_plan(plan_fw); 43 | fftwf_destroy_plan(plan_bw); 44 | } 45 | 46 | /** 47 | * Calculate Fourier transform 48 | * @param in Input data 49 | * @param out Fourier transformed output data 50 | * If in == out, the transformation is done in-place 51 | */ 52 | inline void fft(const std::vector> &in, std::vector> &out) 53 | { 54 | // Ensure in-place transformation 55 | if (in.data() != out.data()) 56 | { 57 | memcpy(out.data(), in.data(), N * sizeof(std::complex)); 58 | } 59 | fftshift2D(out); 60 | fftwf_execute_dft(plan_fw, 61 | reinterpret_cast(out.data()), 62 | reinterpret_cast(out.data())); 63 | 64 | ifftshift2D(out); 65 | 66 | // Scale amplitude as FFTW calculates unscaled coefficients 67 | for (size_t i = 0; i < N; ++i) 68 | { 69 | out[i] /= N; 70 | } 71 | } 72 | 73 | /** 74 | * Calculate inverse Fourier transform 75 | * @param in Input data 76 | * @param out Fourier transformed output data 77 | * If in == out, the transformation is done in-place 78 | */ 79 | inline void ifft(std::vector> &in, std::vector> &out) 80 | { 81 | 82 | // Ensure in-place transformation 83 | if (in.data() != out.data()) 84 | { 85 | memcpy(out.data(), in.data(), N * sizeof(std::complex)); 86 | } 87 | fftshift2D(out); 88 | fftwf_execute_dft(plan_bw, 89 | reinterpret_cast(out.data()), 90 | reinterpret_cast(out.data())); 91 | ifftshift2D(out); 92 | } 93 | 94 | /** 95 | * Shift frequency for FFT 96 | * @param in Input data, the transformation is done in-place 97 | */ 98 | inline void fftshift2D(std::vector> &data) 99 | { 100 | size_t xshift = N2 / 2; 101 | size_t yshift = N1 / 2; 102 | if (N2 % 2 != 0) 103 | { 104 | std::vector> out(N); 105 | for (size_t x = 0; x < N2; x++) 106 | { 107 | size_t outX = (x + xshift) % N2; 108 | for (size_t y = 0; y < N1; y++) 109 | { 110 | size_t outY = (y + yshift) % N1; 111 | out[outX + N2 * outY] = data[x + N2 * y]; 112 | } 113 | } 114 | copy(out.begin(), out.end(), &data[0]); 115 | } 116 | else 117 | { 118 | for (size_t x = 0; x < N2; x++) 119 | { 120 | size_t outX = (x + xshift) % N2; 121 | for (size_t y = 0; y < yshift; y++) 122 | { 123 | size_t outY = (y + yshift) % N1; 124 | swap(data[outX + N2 * outY], data[x + N2 * y]); 125 | } 126 | } 127 | } 128 | } 129 | 130 | /** 131 | * I-Shift frequency for FFT (Invert fftshift) 132 | * @param in Input data, the transformation is done in-place 133 | */ 134 | inline void ifftshift2D(std::vector> &data) 135 | { 136 | size_t xshift = N2 / 2; 137 | if (N2 % 2 != 0) 138 | { 139 | xshift++; 140 | } 141 | size_t yshift = N1 / 2; 142 | if (N1 % 2 != 0) 143 | { 144 | yshift++; 145 | } 146 | if (N % 2 != 0) 147 | { 148 | std::vector> out(N); 149 | for (size_t x = 0; x < N2; x++) 150 | { 151 | size_t outX = (x + xshift) % N2; 152 | for (size_t y = 0; y < N1; y++) 153 | { 154 | size_t outY = (y + yshift) % N1; 155 | out[outX + N2 * outY] = data[x + N2 * y]; 156 | } 157 | } 158 | copy(out.begin(), out.end(), &data[0]); 159 | } 160 | else 161 | { 162 | for (size_t x = 0; x < N2; x++) 163 | { 164 | size_t outX = (x + xshift) % N2; 165 | for (size_t y = 0; y < yshift; y++) 166 | { 167 | size_t outY = (y + yshift) % N1; 168 | swap(data[outX + N2 * outY], data[x + N2 * y]); 169 | } 170 | } 171 | } 172 | } 173 | 174 | /** 175 | * Cast a std::vector into 176 | * a std::vector> with zero 177 | * imaginary parts 178 | * @param in Input std::vector 179 | * @param out Output std::vector> 180 | */ 181 | static void r2c(const std::vector &f, std::vector> &c) 182 | { 183 | for (size_t v = 0; v < f.size(); v++) 184 | { 185 | c[v] = std::complex(f[v], 0.0f); 186 | } 187 | } 188 | 189 | /** 190 | * Cast a std::vector> into 191 | * a std::vector, using the real parts 192 | * @param in Input std::vector> 193 | * @param out Output std::vector (complex.real()) 194 | */ 195 | static void c2r(const std::vector> &c, std::vector &f) 196 | { 197 | for (size_t v = 0; v < c.size(); v++) 198 | { 199 | f[v] = c[v].real(); 200 | } 201 | } 202 | 203 | /** 204 | * Cast a std::vector> into 205 | * a std::vector, using the absolute values 206 | * @param in Input std::vector> 207 | * @param out Output std::vector (complex.real()) 208 | */ 209 | static void c2abs(const std::vector> &c, std::vector &f) 210 | { 211 | for (size_t v = 0; v < c.size(); v++) 212 | { 213 | f[v] = std::abs(c[v]); 214 | } 215 | } 216 | 217 | /** 218 | * Cast a std::vector, into 219 | * a std::vector> with zero 220 | * imaginary parts 221 | * @param in Input std::vector 222 | * @return c Output std::vector> 223 | */ 224 | static std::vector> r2c(const std::vector &f) 225 | { 226 | std::vector> c(f.size()); 227 | for (size_t v = 0; v < f.size(); v++) 228 | { 229 | c[v] = std::complex(f[v], 0.0f); 230 | } 231 | return c; 232 | } 233 | 234 | /** 235 | * Cast a std::vector> into 236 | * a std::vector, using the real parts 237 | * @param in Input std::vector> 238 | * @return f Output std::vector (complex.real()) 239 | */ 240 | static std::vector c2r(const std::vector> &c) 241 | { 242 | std::vector f(c.size()); 243 | for (size_t v = 0; v < c.size(); v++) 244 | { 245 | f[v] = c[v].real(); 246 | } 247 | return f; 248 | } 249 | }; 250 | 251 | #endif 252 | -------------------------------------------------------------------------------- /include/imfilebrowser.h: -------------------------------------------------------------------------------- 1 | /* MIT License 2 | 3 | Copyright (c) 2019-2020 Zhuang Guan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | source: https://github.com/AirGuanZ/imgui-filebrowser 24 | */ 25 | #pragma once 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #ifndef IMGUI_VERSION 39 | # error "include imgui.h before this header" 40 | #endif 41 | 42 | using ImGuiFileBrowserFlags = int; 43 | 44 | enum ImGuiFileBrowserFlags_ 45 | { 46 | ImGuiFileBrowserFlags_SelectDirectory = 1 << 0, // select directory instead of regular file 47 | ImGuiFileBrowserFlags_EnterNewFilename = 1 << 1, // allow user to enter new filename when selecting regular file 48 | ImGuiFileBrowserFlags_NoModal = 1 << 2, // file browsing window is modal by default. specify this to use a popup window 49 | ImGuiFileBrowserFlags_NoTitleBar = 1 << 3, // hide window title bar 50 | ImGuiFileBrowserFlags_NoStatusBar = 1 << 4, // hide status bar at the bottom of browsing window 51 | ImGuiFileBrowserFlags_CloseOnEsc = 1 << 5, // close file browser when pressing 'ESC' 52 | ImGuiFileBrowserFlags_CreateNewDir = 1 << 6, // allow user to create new directory 53 | ImGuiFileBrowserFlags_MultipleSelection = 1 << 7, // allow user to select multiple files. this will hide ImGuiFileBrowserFlags_EnterNewFilename 54 | }; 55 | 56 | namespace ImGui 57 | { 58 | class FileBrowser 59 | { 60 | public: 61 | 62 | // pwd is set to current working directory by default 63 | explicit FileBrowser(ImGuiFileBrowserFlags flags = 0); 64 | 65 | FileBrowser(const FileBrowser ©From); 66 | 67 | FileBrowser &operator=(const FileBrowser ©From); 68 | 69 | // set the window position (in pixels) 70 | // default is centered 71 | void SetWindowPos(int posx, int posy) noexcept; 72 | 73 | // set the window size (in pixels) 74 | // default is (700, 450) 75 | void SetWindowSize(int width, int height) noexcept; 76 | 77 | // set the window title text 78 | void SetTitle(std::string title); 79 | 80 | // open the browsing window 81 | void Open(); 82 | 83 | // close the browsing window 84 | void Close(); 85 | 86 | // the browsing window is opened or not 87 | bool IsOpened() const noexcept; 88 | 89 | // display the browsing window if opened 90 | void Display(); 91 | 92 | // returns true when there is a selected filename and the "ok" button was clicked 93 | bool HasSelected() const noexcept; 94 | 95 | // set current browsing directory 96 | bool SetPwd(const std::filesystem::path &pwd = 97 | std::filesystem::current_path()); 98 | 99 | // get current browsing directory 100 | const std::filesystem::path &GetPwd() const noexcept; 101 | 102 | // returns selected filename. make sense only when HasSelected returns true 103 | // when ImGuiFileBrowserFlags_MultipleSelection is enabled, only one of 104 | // selected filename will be returned 105 | std::filesystem::path GetSelected() const; 106 | 107 | // returns all selected filenames. 108 | // when ImGuiFileBrowserFlags_MultipleSelection is enabled, use this 109 | // instead of GetSelected 110 | std::vector GetMultiSelected() const; 111 | 112 | // set selected filename to empty 113 | void ClearSelected(); 114 | 115 | // (optional) set file type filters. eg. { ".h", ".cpp", ".hpp" } 116 | // ".*" matches any file types 117 | void SetTypeFilters(const std::vector &typeFilters); 118 | 119 | // set currently applied type filter 120 | // default value is 0 (the first type filter) 121 | void SetCurrentTypeFilterIndex(int index); 122 | 123 | // when ImGuiFileBrowserFlags_EnterNewFilename is set 124 | // this function will pre-fill the input dialog with a filename. 125 | void SetInputName(std::string_view input); 126 | 127 | private: 128 | 129 | template 130 | struct ScopeGuard 131 | { 132 | ScopeGuard(Functor&& t) : func(std::move(t)) { } 133 | 134 | ~ScopeGuard() { func(); } 135 | 136 | private: 137 | 138 | Functor func; 139 | }; 140 | 141 | struct FileRecord 142 | { 143 | bool isDir = false; 144 | std::filesystem::path name; 145 | std::string showName; 146 | std::filesystem::path extension; 147 | }; 148 | 149 | static std::string ToLower(const std::string &s); 150 | 151 | void UpdateFileRecords(); 152 | 153 | void SetPwdUncatched(const std::filesystem::path &pwd); 154 | 155 | bool IsExtensionMatched(const std::filesystem::path &extension) const; 156 | 157 | #ifdef _WIN32 158 | static std::uint32_t GetDrivesBitMask(); 159 | #endif 160 | 161 | // for c++17 compatibility 162 | 163 | #if defined(__cpp_lib_char8_t) 164 | static std::string u8StrToStr(std::u8string s); 165 | #endif 166 | static std::string u8StrToStr(std::string s); 167 | 168 | int width_; 169 | int height_; 170 | int posX_; 171 | int posY_; 172 | ImGuiFileBrowserFlags flags_; 173 | 174 | std::string title_; 175 | std::string openLabel_; 176 | 177 | bool openFlag_; 178 | bool closeFlag_; 179 | bool isOpened_; 180 | bool ok_; 181 | bool posIsSet_; 182 | 183 | std::string statusStr_; 184 | 185 | std::vector typeFilters_; 186 | unsigned int typeFilterIndex_; 187 | bool hasAllFilter_; 188 | 189 | std::filesystem::path pwd_; 190 | std::set selectedFilenames_; 191 | 192 | std::vector fileRecords_; 193 | 194 | // IMPROVE: truncate when selectedFilename_.length() > inputNameBuf_.size() - 1 195 | static constexpr size_t INPUT_NAME_BUF_SIZE = 512; 196 | std::unique_ptr> inputNameBuf_; 197 | 198 | std::string openNewDirLabel_; 199 | std::unique_ptr> newDirNameBuf_; 200 | 201 | #ifdef _WIN32 202 | uint32_t drives_; 203 | #endif 204 | }; 205 | } // namespace ImGui 206 | 207 | inline ImGui::FileBrowser::FileBrowser(ImGuiFileBrowserFlags flags) 208 | : width_(700), height_(450), posX_(0), posY_(0), flags_(flags), 209 | openFlag_(false), closeFlag_(false), isOpened_(false), ok_(false), posIsSet_(false), 210 | inputNameBuf_(std::make_unique>()) 211 | { 212 | if(flags_ & ImGuiFileBrowserFlags_CreateNewDir) 213 | { 214 | newDirNameBuf_ = 215 | std::make_unique>(); 216 | } 217 | 218 | inputNameBuf_->front() = '\0'; 219 | inputNameBuf_->back() = '\0'; 220 | SetTitle("file browser"); 221 | SetPwd(std::filesystem::current_path()); 222 | 223 | typeFilters_.clear(); 224 | typeFilterIndex_ = 0; 225 | hasAllFilter_ = false; 226 | 227 | #ifdef _WIN32 228 | drives_ = GetDrivesBitMask(); 229 | #endif 230 | } 231 | 232 | inline ImGui::FileBrowser::FileBrowser(const FileBrowser ©From) 233 | : FileBrowser() 234 | { 235 | *this = copyFrom; 236 | } 237 | 238 | inline ImGui::FileBrowser &ImGui::FileBrowser::operator=( 239 | const FileBrowser ©From) 240 | { 241 | width_ = copyFrom.width_; 242 | height_ = copyFrom.height_; 243 | 244 | posX_ = copyFrom.posX_; 245 | posY_ = copyFrom.posY_; 246 | 247 | flags_ = copyFrom.flags_; 248 | SetTitle(copyFrom.title_); 249 | 250 | openFlag_ = copyFrom.openFlag_; 251 | closeFlag_ = copyFrom.closeFlag_; 252 | isOpened_ = copyFrom.isOpened_; 253 | ok_ = copyFrom.ok_; 254 | posIsSet_ = copyFrom.posIsSet_; 255 | 256 | statusStr_ = ""; 257 | 258 | typeFilters_ = copyFrom.typeFilters_; 259 | typeFilterIndex_ = copyFrom.typeFilterIndex_; 260 | hasAllFilter_ = copyFrom.hasAllFilter_; 261 | 262 | pwd_ = copyFrom.pwd_; 263 | selectedFilenames_ = copyFrom.selectedFilenames_; 264 | 265 | fileRecords_ = copyFrom.fileRecords_; 266 | 267 | *inputNameBuf_ = *copyFrom.inputNameBuf_; 268 | 269 | openNewDirLabel_ = copyFrom.openNewDirLabel_; 270 | if(flags_ & ImGuiFileBrowserFlags_CreateNewDir) 271 | { 272 | newDirNameBuf_ = std::make_unique< 273 | std::array>(); 274 | *newDirNameBuf_ = *copyFrom.newDirNameBuf_; 275 | } 276 | 277 | #ifdef _WIN32 278 | drives_ = copyFrom.drives_; 279 | #endif 280 | 281 | return *this; 282 | } 283 | 284 | inline void ImGui::FileBrowser::SetWindowPos(int posx, int posy) noexcept 285 | { 286 | posX_ = posx; 287 | posY_ = posy; 288 | posIsSet_ = true; 289 | } 290 | 291 | inline void ImGui::FileBrowser::SetWindowSize(int width, int height) noexcept 292 | { 293 | assert(width > 0 && height > 0); 294 | width_ = width; 295 | height_ = height; 296 | } 297 | 298 | inline void ImGui::FileBrowser::SetTitle(std::string title) 299 | { 300 | title_ = std::move(title); 301 | openLabel_ = title_ + "##filebrowser_" + 302 | std::to_string(reinterpret_cast(this)); 303 | openNewDirLabel_ = "new dir##new_dir_" + 304 | std::to_string(reinterpret_cast(this)); 305 | } 306 | 307 | inline void ImGui::FileBrowser::Open() 308 | { 309 | ClearSelected(); 310 | UpdateFileRecords(); 311 | statusStr_ = std::string(); 312 | openFlag_ = true; 313 | closeFlag_ = false; 314 | } 315 | 316 | inline void ImGui::FileBrowser::Close() 317 | { 318 | ClearSelected(); 319 | statusStr_ = std::string(); 320 | closeFlag_ = true; 321 | openFlag_ = false; 322 | } 323 | 324 | inline bool ImGui::FileBrowser::IsOpened() const noexcept 325 | { 326 | return isOpened_; 327 | } 328 | 329 | inline void ImGui::FileBrowser::Display() 330 | { 331 | PushID(this); 332 | ScopeGuard exitThis([this] 333 | { 334 | openFlag_ = false; 335 | closeFlag_ = false; 336 | PopID(); 337 | }); 338 | 339 | if(openFlag_) 340 | { 341 | OpenPopup(openLabel_.c_str()); 342 | } 343 | isOpened_ = false; 344 | 345 | // open the popup window 346 | 347 | if(openFlag_ && (flags_ & ImGuiFileBrowserFlags_NoModal)) 348 | { 349 | if (posIsSet_) 350 | SetNextWindowPos( 351 | ImVec2(static_cast(posX_), static_cast(posY_))); 352 | SetNextWindowSize( 353 | ImVec2(static_cast(width_), static_cast(height_))); 354 | } 355 | else 356 | { 357 | if (posIsSet_) 358 | SetNextWindowPos( 359 | ImVec2(static_cast(posX_), static_cast(posY_)), 360 | ImGuiCond_FirstUseEver); 361 | SetNextWindowSize( 362 | ImVec2(static_cast(width_), static_cast(height_)), 363 | ImGuiCond_FirstUseEver); 364 | } 365 | if(flags_ & ImGuiFileBrowserFlags_NoModal) 366 | { 367 | if(!BeginPopup(openLabel_.c_str())) 368 | { 369 | return; 370 | } 371 | } 372 | else if(!BeginPopupModal(openLabel_.c_str(), nullptr, 373 | flags_ & ImGuiFileBrowserFlags_NoTitleBar ? 374 | ImGuiWindowFlags_NoTitleBar : 0)) 375 | { 376 | return; 377 | } 378 | 379 | isOpened_ = true; 380 | ScopeGuard endPopup([] { EndPopup(); }); 381 | 382 | // display elements in pwd 383 | 384 | #ifdef _WIN32 385 | char currentDrive = static_cast(pwd_.c_str()[0]); 386 | char driveStr[] = { currentDrive, ':', '\0' }; 387 | 388 | PushItemWidth(4 * GetFontSize()); 389 | if(BeginCombo("##select_drive", driveStr)) 390 | { 391 | ScopeGuard guard([&] { EndCombo(); }); 392 | 393 | for(int i = 0; i < 26; ++i) 394 | { 395 | if(!(drives_ & (1 << i))) 396 | { 397 | continue; 398 | } 399 | 400 | char driveCh = static_cast('A' + i); 401 | char selectableStr[] = { driveCh, ':', '\0' }; 402 | bool selected = currentDrive == driveCh; 403 | 404 | if(Selectable(selectableStr, selected) && !selected) 405 | { 406 | char newPwd[] = { driveCh, ':', '\\', '\0' }; 407 | SetPwd(newPwd); 408 | } 409 | } 410 | } 411 | PopItemWidth(); 412 | 413 | SameLine(); 414 | #endif 415 | 416 | int secIdx = 0, newPwdLastSecIdx = -1; 417 | for(const auto &sec : pwd_) 418 | { 419 | #ifdef _WIN32 420 | if(secIdx == 1) 421 | { 422 | ++secIdx; 423 | continue; 424 | } 425 | #endif 426 | 427 | PushID(secIdx); 428 | if(secIdx > 0) 429 | { 430 | SameLine(); 431 | } 432 | if(SmallButton(u8StrToStr(sec.u8string()).c_str())) 433 | { 434 | newPwdLastSecIdx = secIdx; 435 | } 436 | PopID(); 437 | 438 | ++secIdx; 439 | } 440 | 441 | if(newPwdLastSecIdx >= 0) 442 | { 443 | int i = 0; 444 | std::filesystem::path newPwd; 445 | for(const auto &sec : pwd_) 446 | { 447 | if(i++ > newPwdLastSecIdx) 448 | { 449 | break; 450 | } 451 | newPwd /= sec; 452 | } 453 | 454 | #ifdef _WIN32 455 | if(newPwdLastSecIdx == 0) 456 | { 457 | newPwd /= "\\"; 458 | } 459 | #endif 460 | 461 | SetPwd(newPwd); 462 | } 463 | 464 | SameLine(); 465 | 466 | if(SmallButton("*")) 467 | { 468 | UpdateFileRecords(); 469 | 470 | std::set newSelectedFilenames; 471 | for(auto &name : selectedFilenames_) 472 | { 473 | auto it = std::find_if( 474 | fileRecords_.begin(), fileRecords_.end(), 475 | [&](const FileRecord &record) 476 | { 477 | return name == record.name; 478 | }); 479 | 480 | if(it != fileRecords_.end()) 481 | { 482 | newSelectedFilenames.insert(name); 483 | } 484 | } 485 | 486 | if(inputNameBuf_ && (*inputNameBuf_)[0]) 487 | { 488 | newSelectedFilenames.insert(inputNameBuf_->data()); 489 | } 490 | } 491 | 492 | if(newDirNameBuf_) 493 | { 494 | SameLine(); 495 | if(SmallButton("+")) 496 | { 497 | OpenPopup(openNewDirLabel_.c_str()); 498 | (*newDirNameBuf_)[0] = '\0'; 499 | } 500 | 501 | if(BeginPopup(openNewDirLabel_.c_str())) 502 | { 503 | ScopeGuard endNewDirPopup([] { EndPopup(); }); 504 | 505 | InputText("name", newDirNameBuf_->data(), newDirNameBuf_->size()); 506 | SameLine(); 507 | 508 | if(Button("ok") && (*newDirNameBuf_)[0] != '\0') 509 | { 510 | ScopeGuard closeNewDirPopup([] { CloseCurrentPopup(); }); 511 | if(create_directory(pwd_ / newDirNameBuf_->data())) 512 | { 513 | UpdateFileRecords(); 514 | } 515 | else 516 | { 517 | statusStr_ = "failed to create " + 518 | std::string(newDirNameBuf_->data()); 519 | } 520 | } 521 | } 522 | } 523 | 524 | // browse files in a child window 525 | 526 | float reserveHeight = GetFrameHeightWithSpacing(); 527 | std::filesystem::path newPwd; bool setNewPwd = false; 528 | if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) && 529 | (flags_ & ImGuiFileBrowserFlags_EnterNewFilename)) 530 | reserveHeight += GetFrameHeightWithSpacing(); 531 | { 532 | BeginChild("ch", ImVec2(0, -reserveHeight), true, 533 | (flags_ & ImGuiFileBrowserFlags_NoModal) ? 534 | ImGuiWindowFlags_AlwaysHorizontalScrollbar : 0); 535 | ScopeGuard endChild([] { EndChild(); }); 536 | 537 | for(auto &rsc : fileRecords_) 538 | { 539 | if(!rsc.isDir && !IsExtensionMatched(rsc.extension)) 540 | { 541 | continue; 542 | } 543 | 544 | if(!rsc.name.empty() && rsc.name.c_str()[0] == '$') 545 | { 546 | continue; 547 | } 548 | 549 | bool selected = selectedFilenames_.find(rsc.name) 550 | != selectedFilenames_.end(); 551 | 552 | if(Selectable(rsc.showName.c_str(), selected, 553 | ImGuiSelectableFlags_DontClosePopups)) 554 | { 555 | const bool multiSelect = 556 | (flags_ & ImGuiFileBrowserFlags_MultipleSelection) && 557 | IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && 558 | (GetIO().KeyCtrl || GetIO().KeyShift); 559 | 560 | if(selected) 561 | { 562 | if(!multiSelect) 563 | { 564 | selectedFilenames_.clear(); 565 | } 566 | else 567 | { 568 | selectedFilenames_.erase(rsc.name); 569 | } 570 | 571 | (*inputNameBuf_)[0] = '\0'; 572 | } 573 | else if(rsc.name != "..") 574 | { 575 | if((rsc.isDir && (flags_ & ImGuiFileBrowserFlags_SelectDirectory)) || 576 | (!rsc.isDir && !(flags_ & ImGuiFileBrowserFlags_SelectDirectory))) 577 | { 578 | if(multiSelect) 579 | { 580 | selectedFilenames_.insert(rsc.name); 581 | } 582 | else 583 | { 584 | selectedFilenames_ = { rsc.name }; 585 | } 586 | 587 | if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory)) 588 | { 589 | #ifdef _MSC_VER 590 | strcpy_s( 591 | inputNameBuf_->data(), inputNameBuf_->size(), 592 | u8StrToStr(rsc.name.u8string()).c_str()); 593 | #else 594 | std::strncpy(inputNameBuf_->data(), 595 | u8StrToStr(rsc.name.u8string()).c_str(), 596 | inputNameBuf_->size() - 1); 597 | #endif 598 | } 599 | } 600 | } 601 | else 602 | { 603 | if(!multiSelect) 604 | { 605 | selectedFilenames_.clear(); 606 | } 607 | } 608 | } 609 | 610 | if(IsItemClicked(0) && IsMouseDoubleClicked(0)) 611 | { 612 | if(rsc.isDir) 613 | { 614 | setNewPwd = true; 615 | newPwd = (rsc.name != "..") ? (pwd_ / rsc.name) : 616 | pwd_.parent_path(); 617 | } 618 | else if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory)) 619 | { 620 | selectedFilenames_ = { rsc.name }; 621 | ok_ = true; 622 | CloseCurrentPopup(); 623 | } 624 | } 625 | } 626 | } 627 | 628 | if(setNewPwd) 629 | { 630 | SetPwd(newPwd); 631 | } 632 | 633 | if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) && 634 | (flags_ & ImGuiFileBrowserFlags_EnterNewFilename)) 635 | { 636 | PushID(this); 637 | ScopeGuard popTextID([] { PopID(); }); 638 | 639 | PushItemWidth(-1); 640 | if(InputText("", inputNameBuf_->data(), inputNameBuf_->size()) && 641 | inputNameBuf_->at(0) != '\0') 642 | { 643 | selectedFilenames_ = { inputNameBuf_->data() }; 644 | } 645 | PopItemWidth(); 646 | } 647 | 648 | if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory)) 649 | { 650 | if(Button(" ok ") && !selectedFilenames_.empty()) 651 | { 652 | ok_ = true; 653 | CloseCurrentPopup(); 654 | } 655 | } 656 | else 657 | { 658 | if(Button(" ok ")) 659 | { 660 | ok_ = true; 661 | CloseCurrentPopup(); 662 | } 663 | } 664 | 665 | SameLine(); 666 | 667 | bool shouldExit = 668 | Button("cancel") || closeFlag_ || 669 | ((flags_ & ImGuiFileBrowserFlags_CloseOnEsc) && 670 | IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && 671 | IsKeyPressed(ImGuiKey_Escape)); 672 | if(shouldExit) 673 | { 674 | CloseCurrentPopup(); 675 | } 676 | 677 | if(!statusStr_.empty() && !(flags_ & ImGuiFileBrowserFlags_NoStatusBar)) 678 | { 679 | SameLine(); 680 | Text("%s", statusStr_.c_str()); 681 | } 682 | 683 | if(!typeFilters_.empty()) 684 | { 685 | SameLine(); 686 | PushItemWidth(8 * GetFontSize()); 687 | if(BeginCombo( 688 | "##type_filters", typeFilters_[typeFilterIndex_].c_str())) 689 | { 690 | ScopeGuard guard([&] { EndCombo(); }); 691 | 692 | for(size_t i = 0; i < typeFilters_.size(); ++i) 693 | { 694 | bool selected = i == typeFilterIndex_; 695 | if(Selectable(typeFilters_[i].c_str(), selected) && !selected) 696 | { 697 | typeFilterIndex_ = static_cast(i); 698 | } 699 | } 700 | } 701 | PopItemWidth(); 702 | } 703 | } 704 | 705 | inline bool ImGui::FileBrowser::HasSelected() const noexcept 706 | { 707 | return ok_; 708 | } 709 | 710 | inline bool ImGui::FileBrowser::SetPwd(const std::filesystem::path &pwd) 711 | { 712 | try 713 | { 714 | SetPwdUncatched(pwd); 715 | return true; 716 | } 717 | catch(const std::exception &err) 718 | { 719 | statusStr_ = std::string("last error: ") + err.what(); 720 | } 721 | catch(...) 722 | { 723 | statusStr_ = "last error: unknown"; 724 | } 725 | 726 | SetPwdUncatched(std::filesystem::current_path()); 727 | return false; 728 | } 729 | 730 | inline const class std::filesystem::path &ImGui::FileBrowser::GetPwd() const noexcept 731 | { 732 | return pwd_; 733 | } 734 | 735 | inline std::filesystem::path ImGui::FileBrowser::GetSelected() const 736 | { 737 | // when ok_ is true, selectedFilenames_ may be empty if SelectDirectory 738 | // is enabled. return pwd in that case. 739 | if(selectedFilenames_.empty()) 740 | { 741 | return pwd_; 742 | } 743 | return pwd_ / *selectedFilenames_.begin(); 744 | } 745 | 746 | inline std::vector 747 | ImGui::FileBrowser::GetMultiSelected() const 748 | { 749 | if(selectedFilenames_.empty()) 750 | { 751 | return { pwd_ }; 752 | } 753 | 754 | std::vector ret; 755 | ret.reserve(selectedFilenames_.size()); 756 | for(auto &s : selectedFilenames_) 757 | { 758 | ret.push_back(pwd_ / s); 759 | } 760 | 761 | return ret; 762 | } 763 | 764 | inline void ImGui::FileBrowser::ClearSelected() 765 | { 766 | selectedFilenames_.clear(); 767 | (*inputNameBuf_)[0] = '\0'; 768 | ok_ = false; 769 | } 770 | 771 | inline void ImGui::FileBrowser::SetTypeFilters( 772 | const std::vector &_typeFilters) 773 | { 774 | typeFilters_.clear(); 775 | 776 | // remove duplicate filter names due to case unsensitivity on windows 777 | 778 | #ifdef _WIN32 779 | 780 | std::vector typeFilters; 781 | for(auto &rawFilter : _typeFilters) 782 | { 783 | std::string lowerFilter = ToLower(rawFilter); 784 | auto it = std::find(typeFilters.begin(), typeFilters.end(), lowerFilter); 785 | if(it == typeFilters.end()) 786 | { 787 | typeFilters.push_back(std::move(lowerFilter)); 788 | } 789 | } 790 | 791 | #else 792 | 793 | auto &typeFilters = _typeFilters; 794 | 795 | #endif 796 | 797 | // insert auto-generated filter 798 | 799 | if(typeFilters.size() > 1) 800 | { 801 | hasAllFilter_ = true; 802 | std::string allFiltersName = std::string(); 803 | for(size_t i = 0; i < typeFilters.size(); ++i) 804 | { 805 | if(typeFilters[i] == std::string_view(".*")) 806 | { 807 | hasAllFilter_ = false; 808 | break; 809 | } 810 | 811 | if(i > 0) 812 | { 813 | allFiltersName += ","; 814 | } 815 | allFiltersName += typeFilters[i]; 816 | } 817 | 818 | if(hasAllFilter_) 819 | { 820 | typeFilters_.push_back(std::move(allFiltersName)); 821 | } 822 | } 823 | 824 | std::copy( 825 | typeFilters.begin(), typeFilters.end(), 826 | std::back_inserter(typeFilters_)); 827 | 828 | typeFilterIndex_ = 0; 829 | } 830 | 831 | inline void ImGui::FileBrowser::SetCurrentTypeFilterIndex(int index) 832 | { 833 | typeFilterIndex_ = static_cast(index); 834 | } 835 | 836 | inline void ImGui::FileBrowser::SetInputName(std::string_view input) 837 | { 838 | if(flags_ & ImGuiFileBrowserFlags_EnterNewFilename) 839 | { 840 | if(input.size() >= static_cast(INPUT_NAME_BUF_SIZE)) 841 | { 842 | // If input doesn't fit trim off characters 843 | input = input.substr(0, INPUT_NAME_BUF_SIZE - 1); 844 | } 845 | std::copy(input.begin(), input.end(), inputNameBuf_->begin()); 846 | inputNameBuf_->at(input.size()) = '\0'; 847 | selectedFilenames_ = { inputNameBuf_->data() }; 848 | } 849 | } 850 | 851 | inline std::string ImGui::FileBrowser::ToLower(const std::string &s) 852 | { 853 | std::string ret = s; 854 | for(char &c : ret) 855 | { 856 | c = static_cast(std::tolower(c)); 857 | } 858 | return ret; 859 | } 860 | 861 | inline void ImGui::FileBrowser::UpdateFileRecords() 862 | { 863 | fileRecords_ = { FileRecord{ true, "..", "[D] ..", "" } }; 864 | 865 | for(auto &p : std::filesystem::directory_iterator(pwd_)) 866 | { 867 | FileRecord rcd; 868 | 869 | if(p.is_regular_file()) 870 | { 871 | rcd.isDir = false; 872 | } 873 | else if(p.is_directory()) 874 | { 875 | rcd.isDir = true; 876 | } 877 | else 878 | { 879 | continue; 880 | } 881 | 882 | rcd.name = p.path().filename(); 883 | if(rcd.name.empty()) 884 | { 885 | continue; 886 | } 887 | 888 | rcd.extension = p.path().filename().extension(); 889 | 890 | rcd.showName = (rcd.isDir ? "[D] " : "[F] ") + 891 | u8StrToStr(p.path().filename().u8string()); 892 | fileRecords_.push_back(rcd); 893 | } 894 | 895 | std::sort(fileRecords_.begin(), fileRecords_.end(), 896 | [](const FileRecord &L, const FileRecord &R) 897 | { 898 | return (L.isDir ^ R.isDir) ? L.isDir : (L.name < R.name); 899 | }); 900 | } 901 | 902 | inline void ImGui::FileBrowser::SetPwdUncatched(const std::filesystem::path &pwd) 903 | { 904 | pwd_ = absolute(pwd); 905 | UpdateFileRecords(); 906 | selectedFilenames_.clear(); 907 | (*inputNameBuf_)[0] = '\0'; 908 | } 909 | 910 | inline bool ImGui::FileBrowser::IsExtensionMatched( 911 | const std::filesystem::path &_extension) const 912 | { 913 | #ifdef _WIN32 914 | std::filesystem::path extension = ToLower(_extension.string()); 915 | #else 916 | auto &extension = _extension; 917 | #endif 918 | 919 | // no type filters 920 | if(typeFilters_.empty()) 921 | { 922 | return true; 923 | } 924 | 925 | // invalid type filter index 926 | if(static_cast(typeFilterIndex_) >= typeFilters_.size()) 927 | { 928 | return true; 929 | } 930 | 931 | // all type filters 932 | if(hasAllFilter_ && typeFilterIndex_ == 0) 933 | { 934 | for(size_t i = 1; i < typeFilters_.size(); ++i) 935 | { 936 | if(extension == typeFilters_[i]) 937 | { 938 | return true; 939 | } 940 | } 941 | return false; 942 | } 943 | 944 | // universal filter 945 | if(typeFilters_[typeFilterIndex_] == std::string_view(".*")) 946 | { 947 | return true; 948 | } 949 | 950 | // regular filter 951 | return extension == typeFilters_[typeFilterIndex_]; 952 | } 953 | 954 | #if defined(__cpp_lib_char8_t) 955 | inline std::string ImGui::FileBrowser::u8StrToStr(std::u8string s) 956 | { 957 | return std::string(s.begin(), s.end()); 958 | } 959 | #endif 960 | 961 | inline std::string ImGui::FileBrowser::u8StrToStr(std::string s) 962 | { 963 | return s; 964 | } 965 | 966 | #ifdef _WIN32 967 | 968 | #ifndef _INC_WINDOWS 969 | 970 | #ifndef WIN32_LEAN_AND_MEAN 971 | 972 | #define IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN 973 | #define WIN32_LEAN_AND_MEAN 974 | 975 | #endif // #ifndef WIN32_LEAN_AND_MEAN 976 | 977 | #include 978 | 979 | #ifdef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN 980 | #undef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN 981 | #undef WIN32_LEAN_AND_MEAN 982 | #endif // #ifdef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN 983 | 984 | #endif // #ifdef _INC_WINDOWS 985 | 986 | inline std::uint32_t ImGui::FileBrowser::GetDrivesBitMask() 987 | { 988 | DWORD mask = GetLogicalDrives(); 989 | uint32_t ret = 0; 990 | for(int i = 0; i < 26; ++i) 991 | { 992 | if(!(mask & (1 << i))) 993 | { 994 | continue; 995 | } 996 | char rootName[4] = { static_cast('A' + i), ':', '\\', '\0' }; 997 | UINT type = GetDriveTypeA(rootName); 998 | if(type == DRIVE_REMOVABLE || type == DRIVE_FIXED || type == DRIVE_REMOTE) 999 | { 1000 | ret |= (1 << i); 1001 | } 1002 | } 1003 | return ret; 1004 | } 1005 | 1006 | #endif 1007 | -------------------------------------------------------------------------------- /include/ini.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * Copyright (c) 2018 Danijel Durakovic 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | /////////////////////////////////////////////////////////////////////////////// 25 | // 26 | // /mINI/ v0.9.11 27 | // An INI file reader and writer for the modern age. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | // 31 | // A tiny utility library for manipulating INI files with a straightforward 32 | // API and a minimal footprint. It conforms to the (somewhat) standard INI 33 | // format - sections and keys are case insensitive and all leading and 34 | // trailing whitespace is ignored. Comments are lines that begin with a 35 | // semicolon. Trailing comments are allowed on section lines. 36 | // 37 | // Files are read on demand, upon which data is kept in memory and the file 38 | // is closed. This utility supports lazy writing, which only writes changes 39 | // and updates to a file and preserves custom formatting and comments. A lazy 40 | // write invoked by a write() call will read the output file, find what 41 | // changes have been made and update the file accordingly. If you only need to 42 | // generate files, use generate() instead. Section and key order is preserved 43 | // on read, write and insert. 44 | // 45 | /////////////////////////////////////////////////////////////////////////////// 46 | // 47 | // /* BASIC USAGE EXAMPLE: */ 48 | // 49 | // /* read from file */ 50 | // mINI::INIFile file("myfile.ini"); 51 | // mINI::INIStructure ini; 52 | // file.read(ini); 53 | // 54 | // /* read value; gets a reference to actual value in the structure. 55 | // if key or section don't exist, a new empty value will be created */ 56 | // std::string& value = ini["section"]["key"]; 57 | // 58 | // /* read value safely; gets a copy of value in the structure. 59 | // does not alter the structure */ 60 | // std::string value = ini.get("section").get("key"); 61 | // 62 | // /* set or update values */ 63 | // ini["section"]["key"] = "value"; 64 | // 65 | // /* set multiple values */ 66 | // ini["section2"].set({ 67 | // {"key1", "value1"}, 68 | // {"key2", "value2"} 69 | // }); 70 | // 71 | // /* write updates back to file, preserving comments and formatting */ 72 | // file.write(ini); 73 | // 74 | // /* or generate a file (overwrites the original) */ 75 | // file.generate(ini); 76 | // 77 | /////////////////////////////////////////////////////////////////////////////// 78 | // 79 | // Long live the INI file!!! 80 | // 81 | /////////////////////////////////////////////////////////////////////////////// 82 | 83 | #ifndef MINI_INI_H_ 84 | #define MINI_INI_H_ 85 | 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | #include 95 | #include 96 | 97 | namespace mINI 98 | { 99 | namespace INIStringUtil 100 | { 101 | const char* const whitespaceDelimiters = " \t\n\r\f\v"; 102 | inline void trim(std::string& str) 103 | { 104 | str.erase(str.find_last_not_of(whitespaceDelimiters) + 1); 105 | str.erase(0, str.find_first_not_of(whitespaceDelimiters)); 106 | } 107 | #ifndef MINI_CASE_SENSITIVE 108 | inline void toLower(std::string& str) 109 | { 110 | std::transform(str.begin(), str.end(), str.begin(), [](const char c) { 111 | return static_cast(std::tolower(c)); 112 | }); 113 | } 114 | #endif 115 | inline void replace(std::string& str, std::string const& a, std::string const& b) 116 | { 117 | if (!a.empty()) 118 | { 119 | std::size_t pos = 0; 120 | while ((pos = str.find(a, pos)) != std::string::npos) 121 | { 122 | str.replace(pos, a.size(), b); 123 | pos += b.size(); 124 | } 125 | } 126 | } 127 | #ifdef _WIN32 128 | const char* const endl = "\r\n"; 129 | #else 130 | const char* const endl = "\n"; 131 | #endif 132 | } 133 | 134 | template 135 | class INIMap 136 | { 137 | private: 138 | using T_DataIndexMap = std::unordered_map; 139 | using T_DataItem = std::pair; 140 | using T_DataContainer = std::vector; 141 | using T_MultiArgs = typename std::vector>; 142 | 143 | T_DataIndexMap dataIndexMap; 144 | T_DataContainer data; 145 | 146 | inline std::size_t setEmpty(std::string& key) 147 | { 148 | std::size_t index = data.size(); 149 | dataIndexMap[key] = index; 150 | data.emplace_back(key, T()); 151 | return index; 152 | } 153 | 154 | public: 155 | using const_iterator = typename T_DataContainer::const_iterator; 156 | 157 | INIMap() { } 158 | 159 | INIMap(INIMap const& other) 160 | { 161 | std::size_t data_size = other.data.size(); 162 | for (std::size_t i = 0; i < data_size; ++i) 163 | { 164 | auto const& key = other.data[i].first; 165 | auto const& obj = other.data[i].second; 166 | data.emplace_back(key, obj); 167 | } 168 | dataIndexMap = T_DataIndexMap(other.dataIndexMap); 169 | } 170 | 171 | T& operator[](std::string key) 172 | { 173 | INIStringUtil::trim(key); 174 | #ifndef MINI_CASE_SENSITIVE 175 | INIStringUtil::toLower(key); 176 | #endif 177 | auto it = dataIndexMap.find(key); 178 | bool hasIt = (it != dataIndexMap.end()); 179 | std::size_t index = (hasIt) ? it->second : setEmpty(key); 180 | return data[index].second; 181 | } 182 | T get(std::string key) const 183 | { 184 | INIStringUtil::trim(key); 185 | #ifndef MINI_CASE_SENSITIVE 186 | INIStringUtil::toLower(key); 187 | #endif 188 | auto it = dataIndexMap.find(key); 189 | if (it == dataIndexMap.end()) 190 | { 191 | return T(); 192 | } 193 | return T(data[it->second].second); 194 | } 195 | bool has(std::string key) const 196 | { 197 | INIStringUtil::trim(key); 198 | #ifndef MINI_CASE_SENSITIVE 199 | INIStringUtil::toLower(key); 200 | #endif 201 | return (dataIndexMap.count(key) == 1); 202 | } 203 | void set(std::string key, T obj) 204 | { 205 | INIStringUtil::trim(key); 206 | #ifndef MINI_CASE_SENSITIVE 207 | INIStringUtil::toLower(key); 208 | #endif 209 | auto it = dataIndexMap.find(key); 210 | if (it != dataIndexMap.end()) 211 | { 212 | data[it->second].second = obj; 213 | } 214 | else 215 | { 216 | dataIndexMap[key] = data.size(); 217 | data.emplace_back(key, obj); 218 | } 219 | } 220 | void set(T_MultiArgs const& multiArgs) 221 | { 222 | for (auto const& it : multiArgs) 223 | { 224 | auto const& key = it.first; 225 | auto const& obj = it.second; 226 | set(key, obj); 227 | } 228 | } 229 | bool remove(std::string key) 230 | { 231 | INIStringUtil::trim(key); 232 | #ifndef MINI_CASE_SENSITIVE 233 | INIStringUtil::toLower(key); 234 | #endif 235 | auto it = dataIndexMap.find(key); 236 | if (it != dataIndexMap.end()) 237 | { 238 | std::size_t index = it->second; 239 | data.erase(data.begin() + index); 240 | dataIndexMap.erase(it); 241 | for (auto& it2 : dataIndexMap) 242 | { 243 | auto& vi = it2.second; 244 | if (vi > index) 245 | { 246 | vi--; 247 | } 248 | } 249 | return true; 250 | } 251 | return false; 252 | } 253 | void clear() 254 | { 255 | data.clear(); 256 | dataIndexMap.clear(); 257 | } 258 | std::size_t size() const 259 | { 260 | return data.size(); 261 | } 262 | const_iterator begin() const { return data.begin(); } 263 | const_iterator end() const { return data.end(); } 264 | }; 265 | 266 | using INIStructure = INIMap>; 267 | 268 | namespace INIParser 269 | { 270 | using T_ParseValues = std::pair; 271 | 272 | enum class PDataType : char 273 | { 274 | PDATA_NONE, 275 | PDATA_COMMENT, 276 | PDATA_SECTION, 277 | PDATA_KEYVALUE, 278 | PDATA_UNKNOWN 279 | }; 280 | 281 | inline PDataType parseLine(std::string line, T_ParseValues& parseData) 282 | { 283 | parseData.first.clear(); 284 | parseData.second.clear(); 285 | INIStringUtil::trim(line); 286 | if (line.empty()) 287 | { 288 | return PDataType::PDATA_NONE; 289 | } 290 | char firstCharacter = line[0]; 291 | if (firstCharacter == ';') 292 | { 293 | return PDataType::PDATA_COMMENT; 294 | } 295 | if (firstCharacter == '[') 296 | { 297 | auto commentAt = line.find_first_of(';'); 298 | if (commentAt != std::string::npos) 299 | { 300 | line = line.substr(0, commentAt); 301 | } 302 | auto closingBracketAt = line.find_last_of(']'); 303 | if (closingBracketAt != std::string::npos) 304 | { 305 | auto section = line.substr(1, closingBracketAt - 1); 306 | INIStringUtil::trim(section); 307 | parseData.first = section; 308 | return PDataType::PDATA_SECTION; 309 | } 310 | } 311 | auto lineNorm = line; 312 | INIStringUtil::replace(lineNorm, "\\=", " "); 313 | auto equalsAt = lineNorm.find_first_of('='); 314 | if (equalsAt != std::string::npos) 315 | { 316 | auto key = line.substr(0, equalsAt); 317 | INIStringUtil::trim(key); 318 | INIStringUtil::replace(key, "\\=", "="); 319 | auto value = line.substr(equalsAt + 1); 320 | INIStringUtil::trim(value); 321 | parseData.first = key; 322 | parseData.second = value; 323 | return PDataType::PDATA_KEYVALUE; 324 | } 325 | return PDataType::PDATA_UNKNOWN; 326 | } 327 | } 328 | 329 | class INIReader 330 | { 331 | public: 332 | using T_LineData = std::vector; 333 | using T_LineDataPtr = std::shared_ptr; 334 | 335 | private: 336 | std::ifstream fileReadStream; 337 | T_LineDataPtr lineData; 338 | 339 | T_LineData readFile() 340 | { 341 | std::string fileContents; 342 | fileReadStream.seekg(0, std::ios::end); 343 | fileContents.resize(static_cast(fileReadStream.tellg())); 344 | fileReadStream.seekg(0, std::ios::beg); 345 | std::size_t fileSize = fileContents.size(); 346 | fileReadStream.read(&fileContents[0], fileSize); 347 | fileReadStream.close(); 348 | T_LineData output; 349 | if (fileSize == 0) 350 | { 351 | return output; 352 | } 353 | std::string buffer; 354 | buffer.reserve(50); 355 | for (std::size_t i = 0; i < fileSize; ++i) 356 | { 357 | char& c = fileContents[i]; 358 | if (c == '\n') 359 | { 360 | output.emplace_back(buffer); 361 | buffer.clear(); 362 | continue; 363 | } 364 | if (c != '\0' && c != '\r') 365 | { 366 | buffer += c; 367 | } 368 | } 369 | output.emplace_back(buffer); 370 | return output; 371 | } 372 | 373 | public: 374 | INIReader(std::string const& filename, bool keepLineData = false) 375 | { 376 | fileReadStream.open(filename, std::ios::in | std::ios::binary); 377 | if (keepLineData) 378 | { 379 | lineData = std::make_shared(); 380 | } 381 | } 382 | ~INIReader() { } 383 | 384 | bool operator>>(INIStructure& data) 385 | { 386 | if (!fileReadStream.is_open()) 387 | { 388 | return false; 389 | } 390 | T_LineData fileLines = readFile(); 391 | std::string section; 392 | bool inSection = false; 393 | INIParser::T_ParseValues parseData; 394 | for (auto const& line : fileLines) 395 | { 396 | auto parseResult = INIParser::parseLine(line, parseData); 397 | if (parseResult == INIParser::PDataType::PDATA_SECTION) 398 | { 399 | inSection = true; 400 | data[section = parseData.first]; 401 | } 402 | else if (inSection && parseResult == INIParser::PDataType::PDATA_KEYVALUE) 403 | { 404 | auto const& key = parseData.first; 405 | auto const& value = parseData.second; 406 | data[section][key] = value; 407 | } 408 | if (lineData && parseResult != INIParser::PDataType::PDATA_UNKNOWN) 409 | { 410 | if (parseResult == INIParser::PDataType::PDATA_KEYVALUE && !inSection) 411 | { 412 | continue; 413 | } 414 | lineData->emplace_back(line); 415 | } 416 | } 417 | return true; 418 | } 419 | T_LineDataPtr getLines() 420 | { 421 | return lineData; 422 | } 423 | }; 424 | 425 | class INIGenerator 426 | { 427 | private: 428 | std::ofstream fileWriteStream; 429 | 430 | public: 431 | bool prettyPrint = false; 432 | 433 | INIGenerator(std::string const& filename) 434 | { 435 | fileWriteStream.open(filename, std::ios::out | std::ios::binary); 436 | } 437 | ~INIGenerator() { } 438 | 439 | bool operator<<(INIStructure const& data) 440 | { 441 | if (!fileWriteStream.is_open()) 442 | { 443 | return false; 444 | } 445 | if (!data.size()) 446 | { 447 | return true; 448 | } 449 | auto it = data.begin(); 450 | for (;;) 451 | { 452 | auto const& section = it->first; 453 | auto const& collection = it->second; 454 | fileWriteStream 455 | << "[" 456 | << section 457 | << "]"; 458 | if (collection.size()) 459 | { 460 | fileWriteStream << INIStringUtil::endl; 461 | auto it2 = collection.begin(); 462 | for (;;) 463 | { 464 | auto key = it2->first; 465 | INIStringUtil::replace(key, "=", "\\="); 466 | auto value = it2->second; 467 | INIStringUtil::trim(value); 468 | fileWriteStream 469 | << key 470 | << ((prettyPrint) ? " = " : "=") 471 | << value; 472 | if (++it2 == collection.end()) 473 | { 474 | break; 475 | } 476 | fileWriteStream << INIStringUtil::endl; 477 | } 478 | } 479 | if (++it == data.end()) 480 | { 481 | break; 482 | } 483 | fileWriteStream << INIStringUtil::endl; 484 | if (prettyPrint) 485 | { 486 | fileWriteStream << INIStringUtil::endl; 487 | } 488 | } 489 | return true; 490 | } 491 | }; 492 | 493 | class INIWriter 494 | { 495 | private: 496 | using T_LineData = std::vector; 497 | using T_LineDataPtr = std::shared_ptr; 498 | 499 | std::string filename; 500 | 501 | T_LineData getLazyOutput(T_LineDataPtr const& lineData, INIStructure& data, INIStructure& original) 502 | { 503 | T_LineData output; 504 | INIParser::T_ParseValues parseData; 505 | std::string sectionCurrent; 506 | bool parsingSection = false; 507 | bool continueToNextSection = false; 508 | bool discardNextEmpty = false; 509 | bool writeNewKeys = false; 510 | std::size_t lastKeyLine = 0; 511 | for (auto line = lineData->begin(); line != lineData->end(); ++line) 512 | { 513 | if (!writeNewKeys) 514 | { 515 | auto parseResult = INIParser::parseLine(*line, parseData); 516 | if (parseResult == INIParser::PDataType::PDATA_SECTION) 517 | { 518 | if (parsingSection) 519 | { 520 | writeNewKeys = true; 521 | parsingSection = false; 522 | --line; 523 | continue; 524 | } 525 | sectionCurrent = parseData.first; 526 | if (data.has(sectionCurrent)) 527 | { 528 | parsingSection = true; 529 | continueToNextSection = false; 530 | discardNextEmpty = false; 531 | output.emplace_back(*line); 532 | lastKeyLine = output.size(); 533 | } 534 | else 535 | { 536 | continueToNextSection = true; 537 | discardNextEmpty = true; 538 | continue; 539 | } 540 | } 541 | else if (parseResult == INIParser::PDataType::PDATA_KEYVALUE) 542 | { 543 | if (continueToNextSection) 544 | { 545 | continue; 546 | } 547 | if (data.has(sectionCurrent)) 548 | { 549 | auto& collection = data[sectionCurrent]; 550 | auto const& key = parseData.first; 551 | auto const& value = parseData.second; 552 | if (collection.has(key)) 553 | { 554 | auto outputValue = collection[key]; 555 | if (value == outputValue) 556 | { 557 | output.emplace_back(*line); 558 | } 559 | else 560 | { 561 | INIStringUtil::trim(outputValue); 562 | auto lineNorm = *line; 563 | INIStringUtil::replace(lineNorm, "\\=", " "); 564 | auto equalsAt = lineNorm.find_first_of('='); 565 | auto valueAt = lineNorm.find_first_not_of( 566 | INIStringUtil::whitespaceDelimiters, 567 | equalsAt + 1 568 | ); 569 | std::string outputLine = line->substr(0, valueAt); 570 | if (prettyPrint && equalsAt + 1 == valueAt) 571 | { 572 | outputLine += " "; 573 | } 574 | outputLine += outputValue; 575 | output.emplace_back(outputLine); 576 | } 577 | lastKeyLine = output.size(); 578 | } 579 | } 580 | } 581 | else 582 | { 583 | if (discardNextEmpty && line->empty()) 584 | { 585 | discardNextEmpty = false; 586 | } 587 | else if (parseResult != INIParser::PDataType::PDATA_UNKNOWN) 588 | { 589 | output.emplace_back(*line); 590 | } 591 | } 592 | } 593 | if (writeNewKeys || std::next(line) == lineData->end()) 594 | { 595 | T_LineData linesToAdd; 596 | if (data.has(sectionCurrent) && original.has(sectionCurrent)) 597 | { 598 | auto const& collection = data[sectionCurrent]; 599 | auto const& collectionOriginal = original[sectionCurrent]; 600 | for (auto const& it : collection) 601 | { 602 | auto key = it.first; 603 | if (collectionOriginal.has(key)) 604 | { 605 | continue; 606 | } 607 | auto value = it.second; 608 | INIStringUtil::replace(key, "=", "\\="); 609 | INIStringUtil::trim(value); 610 | linesToAdd.emplace_back( 611 | key + ((prettyPrint) ? " = " : "=") + value 612 | ); 613 | } 614 | } 615 | if (!linesToAdd.empty()) 616 | { 617 | output.insert( 618 | output.begin() + lastKeyLine, 619 | linesToAdd.begin(), 620 | linesToAdd.end() 621 | ); 622 | } 623 | if (writeNewKeys) 624 | { 625 | writeNewKeys = false; 626 | --line; 627 | } 628 | } 629 | } 630 | for (auto const& it : data) 631 | { 632 | auto const& section = it.first; 633 | if (original.has(section)) 634 | { 635 | continue; 636 | } 637 | if (prettyPrint && output.size() > 0 && !output.back().empty()) 638 | { 639 | output.emplace_back(); 640 | } 641 | output.emplace_back("[" + section + "]"); 642 | auto const& collection = it.second; 643 | for (auto const& it2 : collection) 644 | { 645 | auto key = it2.first; 646 | auto value = it2.second; 647 | INIStringUtil::replace(key, "=", "\\="); 648 | INIStringUtil::trim(value); 649 | output.emplace_back( 650 | key + ((prettyPrint) ? " = " : "=") + value 651 | ); 652 | } 653 | } 654 | return output; 655 | } 656 | 657 | public: 658 | bool prettyPrint = false; 659 | 660 | INIWriter(std::string const& filename) 661 | : filename(filename) 662 | { 663 | } 664 | ~INIWriter() { } 665 | 666 | bool operator<<(INIStructure& data) 667 | { 668 | struct stat buf; 669 | bool fileExists = (stat(filename.c_str(), &buf) == 0); 670 | if (!fileExists) 671 | { 672 | INIGenerator generator(filename); 673 | generator.prettyPrint = prettyPrint; 674 | return generator << data; 675 | } 676 | INIStructure originalData; 677 | T_LineDataPtr lineData; 678 | bool readSuccess = false; 679 | { 680 | INIReader reader(filename, true); 681 | if ((readSuccess = reader >> originalData)) 682 | { 683 | lineData = reader.getLines(); 684 | } 685 | } 686 | if (!readSuccess) 687 | { 688 | return false; 689 | } 690 | T_LineData output = getLazyOutput(lineData, data, originalData); 691 | std::ofstream fileWriteStream(filename, std::ios::out | std::ios::binary); 692 | if (fileWriteStream.is_open()) 693 | { 694 | if (output.size()) 695 | { 696 | auto line = output.begin(); 697 | for (;;) 698 | { 699 | fileWriteStream << *line; 700 | if (++line == output.end()) 701 | { 702 | break; 703 | } 704 | fileWriteStream << INIStringUtil::endl; 705 | } 706 | } 707 | return true; 708 | } 709 | return false; 710 | } 711 | }; 712 | 713 | class INIFile 714 | { 715 | private: 716 | std::string filename; 717 | 718 | public: 719 | INIFile(std::string const& filename) 720 | : filename(filename) 721 | { } 722 | 723 | ~INIFile() { } 724 | 725 | bool read(INIStructure& data) const 726 | { 727 | if (data.size()) 728 | { 729 | data.clear(); 730 | } 731 | if (filename.empty()) 732 | { 733 | return false; 734 | } 735 | INIReader reader(filename); 736 | return reader >> data; 737 | } 738 | bool generate(INIStructure const& data, bool pretty = false) const 739 | { 740 | if (filename.empty()) 741 | { 742 | return false; 743 | } 744 | INIGenerator generator(filename); 745 | generator.prettyPrint = pretty; 746 | return generator << data; 747 | } 748 | bool write(INIStructure& data, bool pretty = false) const 749 | { 750 | if (filename.empty()) 751 | { 752 | return false; 753 | } 754 | INIWriter writer(filename); 755 | writer.prettyPrint = pretty; 756 | return writer << data; 757 | } 758 | }; 759 | } 760 | 761 | #endif // MINI_INI_H_ 762 | -------------------------------------------------------------------------------- /include/libnpy.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Leon Merten Lohse 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef NPY_HPP_ 24 | #define NPY_HPP_ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | 45 | namespace npy { 46 | 47 | /* Compile-time test for byte order. 48 | If your compiler does not define these per default, you may want to define 49 | one of these constants manually. 50 | Defaults to little endian order. */ 51 | #if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \ 52 | defined(__BIG_ENDIAN__) || \ 53 | defined(__ARMEB__) || \ 54 | defined(__THUMBEB__) || \ 55 | defined(__AARCH64EB__) || \ 56 | defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) 57 | const bool big_endian = true; 58 | #else 59 | const bool big_endian = false; 60 | #endif 61 | 62 | 63 | const char magic_string[] = "\x93NUMPY"; 64 | const size_t magic_string_length = 6; 65 | 66 | const char little_endian_char = '<'; 67 | const char big_endian_char = '>'; 68 | const char no_endian_char = '|'; 69 | 70 | constexpr std::array 71 | endian_chars = {little_endian_char, big_endian_char, no_endian_char}; 72 | constexpr std::array 73 | numtype_chars = {'f', 'i', 'u', 'c'}; 74 | 75 | constexpr char host_endian_char = (big_endian ? 76 | big_endian_char : 77 | little_endian_char); 78 | 79 | /* npy array length */ 80 | typedef unsigned long int ndarray_len_t; 81 | 82 | typedef std::pair version_t; 83 | 84 | struct dtype_t { 85 | const char byteorder; 86 | const char kind; 87 | const unsigned int itemsize; 88 | 89 | // TODO(llohse): implement as constexpr 90 | inline std::string str() const { 91 | const size_t max_buflen = 16; 92 | char buf[max_buflen]; 93 | std::snprintf(buf, max_buflen, "%c%c%u", byteorder, kind, itemsize); 94 | return std::string(buf); 95 | } 96 | 97 | inline std::tuple tie() const { 98 | return std::tie(byteorder, kind, itemsize); 99 | } 100 | }; 101 | 102 | 103 | struct header_t { 104 | const dtype_t dtype; 105 | const bool fortran_order; 106 | const std::vector shape; 107 | }; 108 | 109 | inline void write_magic(std::ostream &ostream, version_t version) { 110 | ostream.write(magic_string, magic_string_length); 111 | ostream.put(version.first); 112 | ostream.put(version.second); 113 | } 114 | 115 | inline version_t read_magic(std::istream &istream) { 116 | char buf[magic_string_length + 2]; 117 | istream.read(buf, magic_string_length + 2); 118 | 119 | if (!istream) { 120 | throw std::runtime_error("io error: failed reading file"); 121 | } 122 | 123 | if (0 != std::memcmp(buf, magic_string, magic_string_length)) 124 | throw std::runtime_error("this file does not have a valid npy format."); 125 | 126 | version_t version; 127 | version.first = buf[magic_string_length]; 128 | version.second = buf[magic_string_length + 1]; 129 | 130 | return version; 131 | } 132 | 133 | const std::unordered_map dtype_map = { 134 | {std::type_index(typeid(float)), {host_endian_char, 'f', sizeof(float)}}, 135 | {std::type_index(typeid(double)), {host_endian_char, 'f', sizeof(double)}}, 136 | {std::type_index(typeid(long double)), {host_endian_char, 'f', sizeof(long double)}}, 137 | {std::type_index(typeid(char)), {no_endian_char, 'i', sizeof(char)}}, 138 | {std::type_index(typeid(signed char)), {no_endian_char, 'i', sizeof(signed char)}}, 139 | {std::type_index(typeid(short)), {host_endian_char, 'i', sizeof(short)}}, 140 | {std::type_index(typeid(int)), {host_endian_char, 'i', sizeof(int)}}, 141 | {std::type_index(typeid(long)), {host_endian_char, 'i', sizeof(long)}}, 142 | {std::type_index(typeid(long long)), {host_endian_char, 'i', sizeof(long long)}}, 143 | {std::type_index(typeid(unsigned char)), {no_endian_char, 'u', sizeof(unsigned char)}}, 144 | {std::type_index(typeid(unsigned short)), {host_endian_char, 'u', sizeof(unsigned short)}}, 145 | {std::type_index(typeid(unsigned int)), {host_endian_char, 'u', sizeof(unsigned int)}}, 146 | {std::type_index(typeid(unsigned long)), {host_endian_char, 'u', sizeof(unsigned long)}}, 147 | {std::type_index(typeid(unsigned long long)), {host_endian_char, 'u', sizeof(unsigned long long)}}, 148 | {std::type_index(typeid(std::complex)), {host_endian_char, 'c', sizeof(std::complex)}}, 149 | {std::type_index(typeid(std::complex)), {host_endian_char, 'c', sizeof(std::complex)}}, 150 | {std::type_index(typeid(std::complex)), {host_endian_char, 'c', sizeof(std::complex)}} 151 | }; 152 | 153 | 154 | // helpers 155 | inline bool is_digits(const std::string &str) { 156 | return std::all_of(str.begin(), str.end(), ::isdigit); 157 | } 158 | 159 | template 160 | inline bool in_array(T val, const std::array &arr) { 161 | return std::find(std::begin(arr), std::end(arr), val) != std::end(arr); 162 | } 163 | 164 | inline dtype_t parse_descr(std::string typestring) { 165 | if (typestring.length() < 3) { 166 | throw std::runtime_error("invalid typestring (length)"); 167 | } 168 | 169 | char byteorder_c = typestring.at(0); 170 | char kind_c = typestring.at(1); 171 | std::string itemsize_s = typestring.substr(2); 172 | 173 | if (!in_array(byteorder_c, endian_chars)) { 174 | throw std::runtime_error("invalid typestring (byteorder)"); 175 | } 176 | 177 | if (!in_array(kind_c, numtype_chars)) { 178 | throw std::runtime_error("invalid typestring (kind)"); 179 | } 180 | 181 | if (!is_digits(itemsize_s)) { 182 | throw std::runtime_error("invalid typestring (itemsize)"); 183 | } 184 | unsigned int itemsize = std::stoul(itemsize_s); 185 | 186 | return {byteorder_c, kind_c, itemsize}; 187 | } 188 | 189 | namespace pyparse { 190 | 191 | /** 192 | Removes leading and trailing whitespaces 193 | */ 194 | inline std::string trim(const std::string &str) { 195 | const std::string whitespace = " \t"; 196 | auto begin = str.find_first_not_of(whitespace); 197 | 198 | if (begin == std::string::npos) 199 | return ""; 200 | 201 | auto end = str.find_last_not_of(whitespace); 202 | 203 | return str.substr(begin, end - begin + 1); 204 | } 205 | 206 | 207 | inline std::string get_value_from_map(const std::string &mapstr) { 208 | size_t sep_pos = mapstr.find_first_of(":"); 209 | if (sep_pos == std::string::npos) 210 | return ""; 211 | 212 | std::string tmp = mapstr.substr(sep_pos + 1); 213 | return trim(tmp); 214 | } 215 | 216 | /** 217 | Parses the string representation of a Python dict 218 | 219 | The keys need to be known and may not appear anywhere else in the data. 220 | */ 221 | inline std::unordered_map parse_dict(std::string in, const std::vector &keys) { 222 | std::unordered_map map; 223 | 224 | if (keys.size() == 0) 225 | return map; 226 | 227 | in = trim(in); 228 | 229 | // unwrap dictionary 230 | if ((in.front() == '{') && (in.back() == '}')) 231 | in = in.substr(1, in.length() - 2); 232 | else 233 | throw std::runtime_error("Not a Python dictionary."); 234 | 235 | std::vector > positions; 236 | 237 | for (auto const &value : keys) { 238 | size_t pos = in.find("'" + value + "'"); 239 | 240 | if (pos == std::string::npos) 241 | throw std::runtime_error("Missing '" + value + "' key."); 242 | 243 | std::pair position_pair{pos, value}; 244 | positions.push_back(position_pair); 245 | } 246 | 247 | // sort by position in dict 248 | std::sort(positions.begin(), positions.end()); 249 | 250 | for (size_t i = 0; i < positions.size(); ++i) { 251 | std::string raw_value; 252 | size_t begin{positions[i].first}; 253 | size_t end{std::string::npos}; 254 | 255 | std::string key = positions[i].second; 256 | 257 | if (i + 1 < positions.size()) 258 | end = positions[i + 1].first; 259 | 260 | raw_value = in.substr(begin, end - begin); 261 | 262 | raw_value = trim(raw_value); 263 | 264 | if (raw_value.back() == ',') 265 | raw_value.pop_back(); 266 | 267 | map[key] = get_value_from_map(raw_value); 268 | } 269 | 270 | return map; 271 | } 272 | 273 | /** 274 | Parses the string representation of a Python boolean 275 | */ 276 | inline bool parse_bool(const std::string &in) { 277 | if (in == "True") 278 | return true; 279 | if (in == "False") 280 | return false; 281 | 282 | throw std::runtime_error("Invalid python boolan."); 283 | } 284 | 285 | /** 286 | Parses the string representation of a Python str 287 | */ 288 | inline std::string parse_str(const std::string &in) { 289 | if ((in.front() == '\'') && (in.back() == '\'')) 290 | return in.substr(1, in.length() - 2); 291 | 292 | throw std::runtime_error("Invalid python string."); 293 | } 294 | 295 | /** 296 | Parses the string represenatation of a Python tuple into a vector of its items 297 | */ 298 | inline std::vector parse_tuple(std::string in) { 299 | std::vector v; 300 | const char seperator = ','; 301 | 302 | in = trim(in); 303 | 304 | if ((in.front() == '(') && (in.back() == ')')) 305 | in = in.substr(1, in.length() - 2); 306 | else 307 | throw std::runtime_error("Invalid Python tuple."); 308 | 309 | std::istringstream iss(in); 310 | 311 | for (std::string token; std::getline(iss, token, seperator);) { 312 | v.push_back(token); 313 | } 314 | 315 | return v; 316 | } 317 | 318 | template 319 | inline std::string write_tuple(const std::vector &v) { 320 | if (v.size() == 0) 321 | return "()"; 322 | 323 | std::ostringstream ss; 324 | 325 | if (v.size() == 1) { 326 | ss << "(" << v.front() << ",)"; 327 | } else { 328 | const std::string delimiter = ", "; 329 | // v.size() > 1 330 | ss << "("; 331 | std::copy(v.begin(), v.end() - 1, std::ostream_iterator(ss, delimiter.c_str())); 332 | ss << v.back(); 333 | ss << ")"; 334 | } 335 | 336 | return ss.str(); 337 | } 338 | 339 | inline std::string write_boolean(bool b) { 340 | if (b) 341 | return "True"; 342 | else 343 | return "False"; 344 | } 345 | 346 | } // namespace pyparse 347 | 348 | 349 | inline header_t parse_header(std::string header) { 350 | /* 351 | The first 6 bytes are a magic string: exactly "x93NUMPY". 352 | The next 1 byte is an unsigned byte: the major version number of the file format, e.g. x01. 353 | The next 1 byte is an unsigned byte: the minor version number of the file format, e.g. x00. Note: the version of the file format is not tied to the version of the numpy package. 354 | The next 2 bytes form a little-endian unsigned short int: the length of the header data HEADER_LEN. 355 | The next HEADER_LEN bytes form the header data describing the array's format. It is an ASCII string which contains a Python literal expression of a dictionary. It is terminated by a newline ('n') and padded with spaces ('x20') to make the total length of the magic string + 4 + HEADER_LEN be evenly divisible by 16 for alignment purposes. 356 | The dictionary contains three keys: 357 | 358 | "descr" : dtype.descr 359 | An object that can be passed as an argument to the numpy.dtype() constructor to create the array's dtype. 360 | "fortran_order" : bool 361 | Whether the array data is Fortran-contiguous or not. Since Fortran-contiguous arrays are a common form of non-C-contiguity, we allow them to be written directly to disk for efficiency. 362 | "shape" : tuple of int 363 | The shape of the array. 364 | For repeatability and readability, this dictionary is formatted using pprint.pformat() so the keys are in alphabetic order. 365 | */ 366 | 367 | // remove trailing newline 368 | if (header.back() != '\n') 369 | throw std::runtime_error("invalid header"); 370 | header.pop_back(); 371 | 372 | // parse the dictionary 373 | std::vector keys{"descr", "fortran_order", "shape"}; 374 | auto dict_map = npy::pyparse::parse_dict(header, keys); 375 | 376 | if (dict_map.size() == 0) 377 | throw std::runtime_error("invalid dictionary in header"); 378 | 379 | std::string descr_s = dict_map["descr"]; 380 | std::string fortran_s = dict_map["fortran_order"]; 381 | std::string shape_s = dict_map["shape"]; 382 | 383 | std::string descr = npy::pyparse::parse_str(descr_s); 384 | dtype_t dtype = parse_descr(descr); 385 | 386 | // convert literal Python bool to C++ bool 387 | bool fortran_order = npy::pyparse::parse_bool(fortran_s); 388 | 389 | // parse the shape tuple 390 | auto shape_v = npy::pyparse::parse_tuple(shape_s); 391 | 392 | std::vector shape; 393 | for (auto item : shape_v) { 394 | ndarray_len_t dim = static_cast(std::stoul(item)); 395 | shape.push_back(dim); 396 | } 397 | 398 | return {dtype, fortran_order, shape}; 399 | } 400 | 401 | 402 | inline std::string 403 | write_header_dict(const std::string &descr, bool fortran_order, const std::vector &shape) { 404 | std::string s_fortran_order = npy::pyparse::write_boolean(fortran_order); 405 | std::string shape_s = npy::pyparse::write_tuple(shape); 406 | 407 | return "{'descr': '" + descr + "', 'fortran_order': " + s_fortran_order + ", 'shape': " + shape_s + ", }"; 408 | } 409 | 410 | inline void write_header(std::ostream &out, const header_t &header) { 411 | std::string header_dict = write_header_dict(header.dtype.str(), header.fortran_order, header.shape); 412 | 413 | size_t length = magic_string_length + 2 + 2 + header_dict.length() + 1; 414 | 415 | version_t version{1, 0}; 416 | if (length >= 255 * 255) { 417 | length = magic_string_length + 2 + 4 + header_dict.length() + 1; 418 | version = {2, 0}; 419 | } 420 | size_t padding_len = 16 - length % 16; 421 | std::string padding(padding_len, ' '); 422 | 423 | // write magic 424 | write_magic(out, version); 425 | 426 | // write header length 427 | if (version == version_t{1, 0}) { 428 | uint8_t header_len_le16[2]; 429 | uint16_t header_len = static_cast(header_dict.length() + padding.length() + 1); 430 | 431 | header_len_le16[0] = (header_len >> 0) & 0xff; 432 | header_len_le16[1] = (header_len >> 8) & 0xff; 433 | out.write(reinterpret_cast(header_len_le16), 2); 434 | } else { 435 | uint8_t header_len_le32[4]; 436 | uint32_t header_len = static_cast(header_dict.length() + padding.length() + 1); 437 | 438 | header_len_le32[0] = (header_len >> 0) & 0xff; 439 | header_len_le32[1] = (header_len >> 8) & 0xff; 440 | header_len_le32[2] = (header_len >> 16) & 0xff; 441 | header_len_le32[3] = (header_len >> 24) & 0xff; 442 | out.write(reinterpret_cast(header_len_le32), 4); 443 | } 444 | 445 | out << header_dict << padding << '\n'; 446 | } 447 | 448 | inline std::string read_header(std::istream &istream) { 449 | // check magic bytes an version number 450 | version_t version = read_magic(istream); 451 | 452 | uint32_t header_length; 453 | if (version == version_t{1, 0}) { 454 | uint8_t header_len_le16[2]; 455 | istream.read(reinterpret_cast(header_len_le16), 2); 456 | header_length = (header_len_le16[0] << 0) | (header_len_le16[1] << 8); 457 | 458 | if ((magic_string_length + 2 + 2 + header_length) % 16 != 0) { 459 | // TODO(llohse): display warning 460 | } 461 | } else if (version == version_t{2, 0}) { 462 | uint8_t header_len_le32[4]; 463 | istream.read(reinterpret_cast(header_len_le32), 4); 464 | 465 | header_length = (header_len_le32[0] << 0) | (header_len_le32[1] << 8) 466 | | (header_len_le32[2] << 16) | (header_len_le32[3] << 24); 467 | 468 | if ((magic_string_length + 2 + 4 + header_length) % 16 != 0) { 469 | // TODO(llohse): display warning 470 | } 471 | } else { 472 | throw std::runtime_error("unsupported file format version"); 473 | } 474 | 475 | auto buf_v = std::vector(); 476 | buf_v.reserve(header_length); 477 | istream.read(buf_v.data(), header_length); 478 | std::string header(buf_v.data(), header_length); 479 | 480 | return header; 481 | } 482 | 483 | inline ndarray_len_t comp_size(const std::vector &shape) { 484 | ndarray_len_t size = 1; 485 | for (ndarray_len_t i : shape) 486 | size *= i; 487 | 488 | return size; 489 | } 490 | 491 | template 492 | inline void 493 | SaveArrayAsNumpy(const std::string &filename, bool fortran_order, unsigned int n_dims, const unsigned long shape[], 494 | const Scalar* data) { 495 | // static_assert(has_typestring::value, "scalar type not understood"); 496 | const dtype_t dtype = dtype_map.at(std::type_index(typeid(Scalar))); 497 | 498 | std::ofstream stream(filename, std::ofstream::binary); 499 | if (!stream) { 500 | throw std::runtime_error("io error: failed to open a file."); 501 | } 502 | 503 | std::vector shape_v(shape, shape + n_dims); 504 | header_t header{dtype, fortran_order, shape_v}; 505 | write_header(stream, header); 506 | 507 | auto size = static_cast(comp_size(shape_v)); 508 | 509 | stream.write(reinterpret_cast(data), sizeof(Scalar) * size); 510 | } 511 | 512 | template 513 | inline void 514 | SaveArrayAsNumpy(const std::string &filename, bool fortran_order, unsigned int n_dims, const unsigned long shape[], 515 | const std::vector &data) { 516 | SaveArrayAsNumpy(filename, fortran_order, n_dims, shape, data.data()); 517 | } 518 | 519 | template 520 | inline void 521 | LoadArrayFromNumpy(const std::string &filename, std::vector &shape, std::vector &data) { 522 | bool fortran_order; 523 | LoadArrayFromNumpy(filename, shape, fortran_order, data); 524 | } 525 | 526 | template 527 | inline void LoadArrayFromNumpy(const std::string &filename, std::vector &shape, bool &fortran_order, 528 | std::vector &data) { 529 | std::ifstream stream(filename, std::ifstream::binary); 530 | if (!stream) { 531 | throw std::runtime_error("io error: failed to open a file."); 532 | } 533 | 534 | std::string header_s = read_header(stream); 535 | 536 | // parse header 537 | header_t header = parse_header(header_s); 538 | 539 | // check if the typestring matches the given one 540 | // static_assert(has_typestring::value, "scalar type not understood"); 541 | const dtype_t dtype = dtype_map.at(std::type_index(typeid(Scalar))); 542 | 543 | if (header.dtype.tie() != dtype.tie()) { 544 | throw std::runtime_error("formatting error: typestrings not matching"); 545 | } 546 | 547 | shape = header.shape; 548 | fortran_order = header.fortran_order; 549 | 550 | // compute the data size based on the shape 551 | auto size = static_cast(comp_size(shape)); 552 | data.resize(size); 553 | 554 | // read the data 555 | stream.read(reinterpret_cast(data.data()), sizeof(Scalar) * size); 556 | } 557 | 558 | } // namespace npy 559 | 560 | #endif // NPY_HPP_ 561 | -------------------------------------------------------------------------------- /src/BoundedThreadPool.hpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifndef BOUNDED_THREAD_POOL_H 15 | #define BOUNDED_THREAD_POOL_H 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | class BoundedThreadPool 26 | { 27 | private: 28 | std::vector threads; 29 | std::queue> tasks; 30 | std::atomic b_running; 31 | std::mutex mtx_queue; 32 | std::condition_variable cnd_buffer_full; 33 | std::condition_variable cnd_buffer_empty; 34 | 35 | void create_threads() 36 | { 37 | for (int i = 0; i < n_threads; i++) 38 | { 39 | threads.push_back(std::thread(&BoundedThreadPool::worker, this)); 40 | } 41 | } 42 | 43 | void wait_for_task() 44 | { 45 | std::unique_lock lock(mtx_queue); 46 | cnd_buffer_empty.wait(lock, [this] 47 | { return !tasks.empty() || !b_running; }); 48 | if (!tasks.empty()) 49 | { 50 | std::function task = std::move(tasks.front()); 51 | tasks.pop(); 52 | cnd_buffer_full.notify_one(); 53 | task(); 54 | } 55 | } 56 | 57 | void worker() 58 | { 59 | while (b_running) 60 | { 61 | try 62 | { 63 | wait_for_task(); 64 | } 65 | catch (const std::exception &e) 66 | { 67 | std::cerr << e.what() << std::endl; 68 | } 69 | } 70 | } 71 | 72 | public: 73 | int n_threads; 74 | int limit; 75 | 76 | template 77 | void push_task(const T &task) 78 | { 79 | std::unique_lock lock(mtx_queue); 80 | cnd_buffer_full.wait(lock, [this] 81 | { return ((int)tasks.size() < limit); }); 82 | tasks.push(std::function(task)); 83 | cnd_buffer_empty.notify_one(); 84 | } 85 | 86 | template 87 | void push_task(const T &task, const A &...args) 88 | { 89 | push_task([task, args...] 90 | { task(args...); }); 91 | } 92 | 93 | void wait_for_completion() 94 | { 95 | std::unique_lock lock(mtx_queue); 96 | cnd_buffer_full.wait(lock, [this] 97 | { return tasks.empty(); }); 98 | } 99 | 100 | void join_threads() 101 | { 102 | for (int i = 0; i < n_threads; i++) 103 | { 104 | threads[i].join(); 105 | } 106 | } 107 | 108 | void init(int n_threads, int limit) 109 | { 110 | int n_threads_max = std::thread::hardware_concurrency(); 111 | if (n_threads > n_threads_max || n_threads < 1) 112 | { 113 | this->n_threads = n_threads_max; 114 | } 115 | else 116 | { 117 | this->n_threads = n_threads; 118 | } 119 | this->limit = limit; 120 | b_running = true; 121 | create_threads(); 122 | } 123 | 124 | explicit BoundedThreadPool(int n_threads) : limit(8) 125 | { 126 | init(n_threads, limit); 127 | } 128 | 129 | explicit BoundedThreadPool(int n_threads, int limit) 130 | { 131 | init(n_threads, limit); 132 | } 133 | 134 | BoundedThreadPool() : b_running(false), n_threads(0), limit(0) {} 135 | 136 | ~BoundedThreadPool() 137 | { 138 | wait_for_completion(); 139 | b_running = false; 140 | cnd_buffer_empty.notify_all(); 141 | join_threads(); 142 | } 143 | }; 144 | 145 | #endif -------------------------------------------------------------------------------- /src/Camera.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #include "Camera.h" 15 | #include "MerlinInterface.h" 16 | #include "TimepixInterface.h" 17 | 18 | using namespace CAMERA; 19 | 20 | void Camera_BASE::init_uv_default() 21 | { 22 | u.resize(nx_cam); 23 | v.resize(ny_cam); 24 | 25 | for (int i = 0; i < nx_cam; i++) 26 | { 27 | u[i] = i; 28 | } 29 | 30 | for (int i = 0; i < ny_cam; i++) 31 | { 32 | v[i] = i; 33 | } 34 | }; 35 | 36 | Default_configurations::Default_configurations() 37 | { 38 | hws_ptr = &hws[0]; 39 | hws[MERLIN] = Camera(); 40 | hws[TIMEPIX] = Camera(); 41 | }; 42 | 43 | CAMERA::Camera_BASE &Default_configurations::operator[](unsigned int index) 44 | { 45 | if (index >= hws.size()) 46 | { 47 | perror("CAMERA::Default_configurations::operator[]: Index out of range"); 48 | } 49 | return hws_ptr[index]; 50 | } 51 | -------------------------------------------------------------------------------- /src/Camera.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifndef CAMERA_H 15 | #define CAMERA_H 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | // Only forward declaration for Ricom class 23 | class Ricom; 24 | 25 | namespace CAMERA 26 | { 27 | enum Camera_model 28 | { 29 | MERLIN, 30 | TIMEPIX, 31 | MODELS_COUNT 32 | }; 33 | 34 | enum Camera_type 35 | { 36 | FRAME_BASED, 37 | EVENT_BASED 38 | }; 39 | 40 | class Camera_BASE 41 | { 42 | public: 43 | Camera_model model; 44 | Camera_type type; 45 | int nx_cam; 46 | int ny_cam; 47 | bool swap_endian; 48 | int depth; 49 | int dwell_time; 50 | std::vector u; 51 | std::vector v; 52 | void init_uv_default(); 53 | 54 | Camera_BASE() : model(MERLIN), 55 | type(FRAME_BASED), 56 | nx_cam(256), 57 | ny_cam(256), 58 | swap_endian(true), 59 | depth(1), 60 | dwell_time(1000){}; 61 | }; 62 | 63 | // primary template 64 | template 65 | class Camera : public CameraInterface, public Camera_BASE 66 | { 67 | }; 68 | 69 | // specialization for frame based camera 70 | template 71 | class Camera : public CameraInterface, public Camera_BASE 72 | { 73 | public: 74 | Camera(); 75 | explicit Camera(Camera_BASE &cam); 76 | void run(Ricom *ricom); 77 | template 78 | void read_frame(std::vector &data, bool b_first); 79 | }; 80 | 81 | // specialization for event based camera 82 | template 83 | class Camera : public CameraInterface, public Camera_BASE 84 | { 85 | public: 86 | Camera(); 87 | explicit Camera(Camera_BASE &cam); 88 | void run(Ricom *ricom); 89 | void read_frame_com(std::atomic &idx, std::vector &dose_map, 90 | std::vector &sumx_map, std::vector &sumy_map, 91 | std::vector &stem_map, bool b_stem, 92 | std::array &offset, std::array &radius, 93 | size_t first_frame, size_t end_frame); 94 | void read_frame_com_cbed(std::atomic &idx, std::vector &dose_map, 95 | std::vector &sumx_map, std::vector &sumy_map, 96 | std::vector &stem_map, bool b_stem, 97 | std::array &offset, std::array &radius, 98 | std::vector &frame, size_t frame_id, 99 | size_t first_frame, size_t end_frame); 100 | }; 101 | 102 | class Default_configurations 103 | { 104 | public: 105 | Default_configurations(); 106 | CAMERA::Camera_BASE &operator[](unsigned int index); 107 | std::array hws; 108 | 109 | private: 110 | CAMERA::Camera_BASE *hws_ptr; 111 | }; 112 | } 113 | 114 | #endif // CAMERA_H -------------------------------------------------------------------------------- /src/FileConnector.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #include "FileConnector.h" 15 | 16 | void FileConnector::open_file() 17 | { 18 | if (!path.empty()) 19 | { 20 | file_size = std::filesystem::file_size(path); 21 | stream.open(path, std::ios::in | std::ios::binary); 22 | if (stream.is_open()) 23 | { 24 | reset_file(); 25 | } 26 | else 27 | { 28 | std::cout << "FileConnector::open_file(): Error opening file!" << std::endl; 29 | } 30 | } 31 | else 32 | { 33 | std::cout << "FileConnector::open_file(): Path argument is empty!" << std::endl; 34 | } 35 | } 36 | 37 | void FileConnector::close_file() 38 | { 39 | if (stream.is_open()) 40 | { 41 | stream.close(); 42 | } 43 | } 44 | 45 | // Reading data stream from File 46 | void FileConnector::read_data(char *buffer, size_t data_size) 47 | { 48 | stream.read(buffer, data_size); 49 | pos += data_size; 50 | // Reset file to the beginning for repeat reading 51 | if (file_size - pos < data_size) 52 | { 53 | reset_file(); 54 | } 55 | }; 56 | 57 | void FileConnector::reset_file() 58 | { 59 | pos = 0; 60 | stream.clear(); 61 | stream.seekg(0, std::ios::beg); 62 | } 63 | 64 | FileConnector::FileConnector(): path(), stream(), file_size(0), pos(0) {}; -------------------------------------------------------------------------------- /src/FileConnector.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifndef FILE_CONNECTOR_H 15 | #define FILE_CONNECTOR_H 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | class FileConnector 24 | { 25 | public: 26 | std::filesystem::path path; 27 | void open_file(); 28 | void close_file(); 29 | void read_data(char *buffer, size_t data_size); 30 | FileConnector(); 31 | 32 | private: 33 | std::ifstream stream; 34 | std::uintmax_t file_size; 35 | std::uintmax_t pos; 36 | void reset_file(); 37 | }; 38 | #endif // FILE_CONNECTOR_H -------------------------------------------------------------------------------- /src/GuiUtils.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #include "GuiUtils.h" 15 | 16 | template 17 | void save_numpy(std::string *path, int nx, int ny, std::vector *data) 18 | { 19 | std::string ext = std::filesystem::path(*path).extension().string(); 20 | if (ext != ".npy") 21 | { 22 | *path += ".npy"; 23 | } 24 | const std::vector shape{static_cast(ny), static_cast(nx)}; 25 | npy::SaveArrayAsNumpy(path->c_str(), false, shape.size(), shape.data(), *data); 26 | } 27 | template void save_numpy(std::string *path, int nx, int ny, std::vector *data); 28 | template void save_numpy>(std::string *path, int nx, int ny, std::vector> *data); 29 | 30 | void save_image(std::string *path, SDL_Surface *sdl_srf) 31 | { 32 | std::string ext = std::filesystem::path(*path).extension().string(); 33 | if ((ext != ".png") && (ext != ".PNG")) 34 | { 35 | *path += ".png"; 36 | } 37 | IMG_SavePNG(sdl_srf, path->c_str()); 38 | } 39 | 40 | // Vertical Splitter Container 41 | void v_splitter(float thickness, float &size0, const float &min_h, const float &max_h, const float &offset) 42 | { 43 | 44 | ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[ImGuiCol_ScrollbarGrab]); 45 | ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyle().Colors[ImGuiCol_ScrollbarGrabHovered]); 46 | ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyle().Colors[ImGuiCol_ScrollbarGrabActive]); 47 | ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, ImGui::GetStyle().ScrollbarRounding); 48 | ImGui::Button("v", ImVec2(-1, thickness)); 49 | ImGui::PopStyleColor(3); 50 | ImGui::PopStyleVar(1); 51 | if (ImGui::IsItemHovered() || ImGui::IsItemActive()) 52 | { 53 | ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); 54 | } 55 | 56 | if (ImGui::IsItemActive()) 57 | { 58 | float d = ImGui::GetMousePos().y - offset; 59 | if (d < min_h) 60 | size0 = min_h; 61 | else if (d > max_h) 62 | size0 = max_h; 63 | else 64 | size0 = d; 65 | } 66 | }; 67 | 68 | Main_Dock::Main_Dock(ImGuiID dock_id) 69 | { 70 | this->dock_id = dock_id; 71 | } 72 | 73 | void Main_Dock::render(ImVec2 pos, ImVec2 size) 74 | { 75 | ImGui::SetNextWindowPos(pos); 76 | ImGui::SetNextWindowSize(size); 77 | ImGui::Begin("DockWindow", nullptr, window_flags); 78 | ImGui::DockSpace(dock_id); 79 | 80 | static auto first_time = true; 81 | if (first_time) 82 | { 83 | first_time = false; 84 | ImGui::DockBuilderRemoveNode(dock_id); // clear any previous layout 85 | ImGui::DockBuilderAddNode(dock_id, dockspace_flags); 86 | ImGui::DockBuilderSetNodePos(dock_id, pos); 87 | ImGui::DockBuilderSetNodeSize(dock_id, size); 88 | ImGui::DockBuilderDockWindow("RICOM", dock_id); 89 | ImGui::DockBuilderFinish(dock_id); 90 | } 91 | ImGui::End(); 92 | } 93 | 94 | namespace SDL_Utils 95 | { 96 | // Draw a pixel on the surface at (x, y) for a given colormap 97 | void draw_pixel(SDL_Surface *surface, int x, int y, float val, int col_map) 98 | { 99 | tinycolormap::Color c = tinycolormap::GetColor(val, tinycolormap::ColormapType(col_map)); 100 | Uint32 px = SDL_MapRGB(surface->format, (Uint8)(c.ri()), (Uint8)(c.gi()), (Uint8)(c.bi())); 101 | Uint32 *const target_pixel = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch + x * surface->format->BytesPerPixel); 102 | *target_pixel = px; 103 | } 104 | 105 | void draw_pixel(SDL_Surface *surface, int x, int y, float ang, float mag, int col_map) 106 | { 107 | tinycolormap::Color c = mag * tinycolormap::GetColor(ang, tinycolormap::ColormapType(col_map)); 108 | Uint32 px = SDL_MapRGB(surface->format, (Uint8)(c.ri()), (Uint8)(c.gi()), (Uint8)(c.bi())); 109 | Uint32 *const target_pixel = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch + x * surface->format->BytesPerPixel); 110 | *target_pixel = px; 111 | } 112 | } -------------------------------------------------------------------------------- /src/GuiUtils.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifndef GUI_UTILS_H 15 | #define GUI_UTILS_H 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | #include "imgui.h" 25 | #include "imgui_internal.h" 26 | 27 | #include "libnpy.hpp" 28 | #include "tinycolormap.hpp" 29 | class Main_Dock 30 | { 31 | public: 32 | ImGuiID dock_id; 33 | const ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_DockSpace; 34 | const ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; 35 | explicit Main_Dock(ImGuiID dock_id); 36 | void render(ImVec2 pos, ImVec2 size); 37 | }; 38 | 39 | template 40 | void save_numpy(std::string *path, int nx, int ny, std::vector *data); 41 | 42 | void save_image(std::string *path, SDL_Surface *sdl_srf); 43 | 44 | void v_splitter(float thickness,float &size0, const float &min_h, const float &max_h, const float &offset); 45 | namespace SDL_Utils 46 | { 47 | void draw_pixel(SDL_Surface *surface, int x, int y, float val, int color_map); 48 | void draw_pixel(SDL_Surface *surface, int x, int y, float ang, float mag, int color_map); 49 | } 50 | 51 | #endif // GUI_UTILS_H -------------------------------------------------------------------------------- /src/ImGuiINI.hpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #include "imgui.h" 15 | #include "ini.h" 16 | #include 17 | 18 | namespace ImGuiINI 19 | { 20 | // Styles 21 | void set_style(int style_idx) 22 | { 23 | switch (style_idx) 24 | { 25 | case 0: 26 | ImGui::StyleColorsDark(); 27 | break; 28 | case 1: 29 | ImGui::StyleColorsLight(); 30 | break; 31 | case 2: 32 | ImGui::StyleColorsClassic(); 33 | break; 34 | } 35 | } 36 | 37 | bool ShowStyleSelector(const char *label, int &style_idx, mINI::INIStructure &ini_cfg) 38 | { 39 | if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0")) 40 | { 41 | set_style(style_idx); 42 | ini_cfg["Appearance"]["Style"] = std::to_string(style_idx); 43 | return true; 44 | } 45 | return false; 46 | } 47 | 48 | // Fonts 49 | const char *font_names[6] = { 50 | "Karla-Regular", 51 | "Roboto-Medium", 52 | "Cousine-Regular", 53 | "DroidSans", 54 | "ProggyClean", 55 | "ProggyTiny"}; 56 | 57 | bool ShowFontSelector(const char *label, int &selectedFont, mINI::INIStructure &ini_cfg) 58 | { 59 | ImGuiIO &io = ImGui::GetIO(); 60 | ImFont *font_current = ImGui::GetFont(); 61 | if (ImGui::BeginCombo(label, font_names[selectedFont])) 62 | { 63 | for (int n = 0; n < io.Fonts->Fonts.Size; n++) 64 | { 65 | ImFont *font = io.Fonts->Fonts[n]; 66 | ImGui::PushID((void *)font); 67 | if (ImGui::Selectable(font_names[n], font == font_current)) 68 | { 69 | io.FontDefault = font; 70 | selectedFont = n; 71 | } 72 | ImGui::PopID(); 73 | } 74 | ImGui::EndCombo(); 75 | ini_cfg["Appearance"]["Font"] = std::to_string(selectedFont); 76 | return true; 77 | } 78 | return false; 79 | } 80 | 81 | void set_font(int font_idx) 82 | { 83 | ImGuiIO &io = ImGui::GetIO(); 84 | ImFont *font = io.Fonts->Fonts[font_idx]; 85 | io.FontDefault = font; 86 | } 87 | 88 | // Helper functions for conversions 89 | template 90 | std::vector split2T(std::string &s, char delimiter) 91 | { 92 | std::vector values; 93 | std::string token; 94 | std::istringstream tokenStream(s); 95 | while (std::getline(tokenStream, token, delimiter)) 96 | { 97 | values.push_back((T)std::stod(token)); 98 | } 99 | return values; 100 | } 101 | 102 | std::string ImVec2string(const ImVec4 &v) 103 | { 104 | std::string s = std::to_string(v.x) + "," + std::to_string(v.y) + "," + std::to_string(v.z) + "," + std::to_string(v.w); 105 | return s; 106 | } 107 | 108 | std::string ImVec2string(const ImVec2 &v) 109 | { 110 | std::string s = std::to_string(v.x) + "," + std::to_string(v.y); 111 | return s; 112 | } 113 | 114 | // Check if INI has a stored value for a given key and update the value if it does 115 | // Strings 116 | void check_ini_setting(mINI::INIStructure &ini_cfg, std::string section, std::string key, char *value) 117 | { 118 | if (ini_cfg.has(section)) 119 | { 120 | if (ini_cfg[section].has(key)) 121 | { 122 | value = &ini_cfg[section][key][0]; 123 | } 124 | ini_cfg[section][key] = std::string(value); 125 | } 126 | } 127 | 128 | void check_ini_setting(mINI::INIStructure &ini_cfg, std::string section, std::string key, std::string value) 129 | { 130 | if (ini_cfg.has(section)) 131 | { 132 | if (ini_cfg[section].has(key)) 133 | { 134 | value = ini_cfg[section][key]; 135 | } 136 | ini_cfg[section][key] = value; 137 | } 138 | } 139 | 140 | // General Numeric types 141 | template 142 | void check_ini_setting(mINI::INIStructure &ini_cfg, std::string section, std::string key, T &value) 143 | { 144 | if (ini_cfg.has(section)) 145 | { 146 | if (ini_cfg[section].has(key)) 147 | { 148 | value = (T)stod(ini_cfg[section][key]); 149 | } 150 | ini_cfg[section][key] = std::to_string(value); 151 | } 152 | } 153 | 154 | // ImVec4 155 | void check_ini_setting(mINI::INIStructure &ini_cfg, std::string section, std::string key, ImVec4 &value) 156 | { 157 | if (ini_cfg.has(section)) 158 | { 159 | if (ini_cfg[section].has(key)) 160 | { 161 | std::vector val_vec = split2T(ini_cfg[section][key], ','); 162 | value = ImVec4(val_vec[0], val_vec[1], val_vec[2], val_vec[3]); 163 | } 164 | ini_cfg[section][key] = ImVec2string(value); 165 | } 166 | } 167 | 168 | // ImVec2 169 | void check_ini_setting(mINI::INIStructure &ini_cfg, std::string section, std::string key, ImVec2 &value) 170 | { 171 | if (ini_cfg.has(section)) 172 | { 173 | if (ini_cfg[section].has(key)) 174 | { 175 | std::vector val_vec = split2T(ini_cfg[section][key], ','); 176 | value = ImVec2(val_vec[0], val_vec[1]); 177 | } 178 | ini_cfg[section][key] = ImVec2string(value); 179 | } 180 | } 181 | } -------------------------------------------------------------------------------- /src/ImGuiImageWindow.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifdef _MSC_VER 15 | #define _CRT_SECURE_NO_DEPRECATE 16 | #define _CRT_SECURE_NO_WARNINGS 17 | #pragma warning(disable : 4067) 18 | #pragma warning(disable : 4333) 19 | #pragma warning(disable : 4312) 20 | #endif 21 | 22 | #if defined(__GNUC__) 23 | #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" 24 | #pragma GCC diagnostic ignored "-Wformat-security" 25 | #endif 26 | 27 | #if defined(__clang__) 28 | #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" 29 | #pragma GCC diagnostic ignored "-Wformat-security" 30 | #endif 31 | 32 | #include "ImGuiImageWindow.h" 33 | 34 | template 35 | inline T pw(T val, T power) 36 | { 37 | return copysign(1.0, val) * pow(abs(val), power); 38 | } 39 | 40 | namespace cmap = tinycolormap; 41 | 42 | GIM_Flags operator|(GIM_Flags lhs, GIM_Flags rhs) 43 | { 44 | return static_cast( 45 | static_cast>(lhs) | 46 | static_cast>(rhs)); 47 | } 48 | 49 | GIM_Flags operator&(GIM_Flags lhs, GIM_Flags rhs) 50 | { 51 | return static_cast( 52 | static_cast>(lhs) & 53 | static_cast>(rhs)); 54 | } 55 | 56 | template 57 | bool ImGuiImageWindow::has(GIM_Flags flag) 58 | { 59 | return static_cast(flags & flag); 60 | } 61 | 62 | // Redraws the entire image 63 | template 64 | void ImGuiImageWindow::render_image() 65 | { 66 | if (b_data_set) 67 | { 68 | if (!auto_render) 69 | { 70 | set_min_max(); 71 | } 72 | for (int y = 0; y < ny; y++) 73 | { 74 | for (int x = 0; x < nx; x++) 75 | { 76 | set_pixel(x, y); 77 | } 78 | } 79 | } 80 | } 81 | 82 | // Redraws the entire ricom image from line y0 to line ye 83 | template 84 | void ImGuiImageWindow::render_image(int last_idr) 85 | { 86 | int last_yt = (last_idr / nx); 87 | if (b_data_set) 88 | { 89 | set_min_max(last_idr); 90 | for (int y = (std::max)(0, this->last_y - render_update_offset); y < (std::min)(last_yt + render_update_offset, ny); y++) 91 | { 92 | for (int x = 0; x < nx; x++) 93 | { 94 | set_pixel(x, y); 95 | } 96 | } 97 | } 98 | this->last_y = last_yt; 99 | this->last_idr = last_idr; 100 | } 101 | 102 | template <> 103 | void ImGuiImageWindow::set_pixel(int idx, int idy) 104 | { 105 | // determine location index of value in memory 106 | int idr = idy * nx + idx; 107 | float val = pw((data->at(idr) - data_min) / data_range, power); 108 | 109 | // Update pixel at location 110 | SDL_Utils::draw_pixel(sdl_srf, idx, idy, val, data_cmap); 111 | } 112 | 113 | template <> 114 | void ImGuiImageWindow>::set_pixel(int idx, int idy) 115 | { 116 | // determine location index of value in memory 117 | int idr = idy * nx + idx; 118 | 119 | // Get magnitude and angle from complex 120 | float mag = (abs(data->at(idr)) - data_min) / data_range; 121 | float ang = arg(data->at(idr)); 122 | ang = (ang / M_PI + 1) / 2; 123 | mag = pw(mag, power); 124 | 125 | // Update pixel at location 126 | SDL_Utils::draw_pixel(sdl_srf, idx, idy, ang, mag, data_cmap); 127 | } 128 | 129 | template <> 130 | float ImGuiImageWindow::get_val(int idr) 131 | { 132 | return (*data)[idr]; 133 | } 134 | 135 | template <> 136 | float ImGuiImageWindow>::get_val(int idr) 137 | { 138 | return abs((*data)[idr]); 139 | } 140 | 141 | template 142 | void ImGuiImageWindow::set_min_max(int last_idr) 143 | { 144 | for (int idr = this->last_idr; idr < last_idr; idr++) 145 | { 146 | float val = get_val(idr); 147 | if (val < data_min) 148 | { 149 | data_min = val; 150 | data_range = data_max - data_min; 151 | b_trigger_update = true; 152 | } 153 | if (val > data_max) 154 | { 155 | data_max = val; 156 | data_range = data_max - data_min; 157 | b_trigger_update = true; 158 | } 159 | } 160 | } 161 | 162 | template 163 | void ImGuiImageWindow::set_min_max() 164 | { 165 | for (int idr = 0; idr < nxy; idr++) 166 | { 167 | float val = get_val(idr); 168 | if (val < data_min) 169 | { 170 | data_min = val; 171 | data_range = data_max - data_min; 172 | b_trigger_update = true; 173 | } 174 | if (val > data_max) 175 | { 176 | data_max = val; 177 | data_range = data_max - data_min; 178 | b_trigger_update = true; 179 | } 180 | } 181 | } 182 | 183 | template 184 | ImGuiImageWindow::ImGuiImageWindow(const std::string &title, GLuint *tex_id, bool auto_render, int data_cmap, GIM_Flags flags, bool *visible) 185 | { 186 | 187 | this->title = title; 188 | this->flags = flags; 189 | this->tex_id = tex_id; 190 | this->pb_open = visible; 191 | this->auto_render = auto_render; 192 | this->data_cmap = data_cmap; 193 | this->last_y = 0; 194 | this->last_idr = 0; 195 | this->last_img = 0; 196 | this->zoom = 1.0f; 197 | this->power = 1.0f; 198 | this->ny = 1; 199 | this->nx = 1; 200 | this->nxy = 1; 201 | this->render_update_offset = 0; 202 | this->b_trigger_update = false; 203 | saveFileDialog = ImGui::FileBrowser(ImGuiFileBrowserFlags_EnterNewFilename | ImGuiFileBrowserFlags_CreateNewDir); 204 | saveFileDialog.SetTitle("Save " + title + " image as .png"); 205 | saveDataDialog = ImGui::FileBrowser(ImGuiFileBrowserFlags_EnterNewFilename | ImGuiFileBrowserFlags_CreateNewDir); 206 | saveDataDialog.SetTitle("Save " + title + "-data as numpy array (.npy)"); 207 | 208 | uv_min = ImVec2(0.0f, 0.0f); // Top-left 209 | uv_max = ImVec2(1.0f, 1.0f); // Lower-right 210 | tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint 211 | border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); // 50% opaque white 212 | sdl_srf = SDL_CreateRGBSurface(0, this->nx, this->ny, 32, 0, 0, 0, 0); 213 | if (sdl_srf == NULL) 214 | { 215 | std::cout << "Surface could not be created! SDL Error: " << SDL_GetError() << std::endl; 216 | } 217 | this->b_data_set = false; 218 | } 219 | 220 | template 221 | void ImGuiImageWindow::set_data(int width, int height, std::vector *data) 222 | { 223 | this->data = data; 224 | set_nx_ny(width, height); 225 | reset_limits(); 226 | if (!auto_render) 227 | { 228 | render_image(); 229 | }; 230 | b_data_set = true; 231 | } 232 | 233 | template 234 | void ImGuiImageWindow::reset_limits() 235 | { 236 | last_y = 0; 237 | last_idr = 0; 238 | last_img = 0; 239 | data_min = FLT_MAX; 240 | data_max = -FLT_MAX; 241 | data_range = FLT_MAX; 242 | } 243 | 244 | template 245 | void ImGuiImageWindow::set_nx_ny(int width, int height) 246 | { 247 | this->nx = width; 248 | this->ny = height; 249 | this->nxy = height * width; 250 | 251 | data_fft.resize(nxy); 252 | data_fft_f.resize(nxy); 253 | data_val.resize(nxy); 254 | 255 | reset_limits(); 256 | 257 | sdl_srf = SDL_CreateRGBSurface(0, this->nx, this->ny, 32, 0, 0, 0, 0); 258 | if (sdl_srf == NULL) 259 | { 260 | std::cout << "Surface could not be created! SDL Error: " << SDL_GetError() << std::endl; 261 | } 262 | } 263 | 264 | template 265 | void ImGuiImageWindow::render_window(bool b_redraw, int last_y, int render_update_offset, bool b_trigger_update) 266 | { 267 | this->render_update_offset = render_update_offset; 268 | render_window(b_redraw, last_y, b_trigger_update); 269 | } 270 | 271 | template 272 | void ImGuiImageWindow::reset_min_max() 273 | { 274 | data_min = FLT_MAX; 275 | data_max = -FLT_MAX; 276 | data_range = FLT_MAX; 277 | } 278 | 279 | template <> 280 | void ImGuiImageWindow::compute_fft() 281 | { 282 | FFT2D fft2d(ny, nx); 283 | FFT2D::r2c(*data, data_val); 284 | fft2d.fft(data_val, data_fft); 285 | FFT2D::c2abs(data_fft, data_fft_f); 286 | } 287 | 288 | template <> 289 | void ImGuiImageWindow>::compute_fft() 290 | { 291 | FFT2D fft2d(ny, nx); 292 | fft2d.fft(*data, data_fft); 293 | FFT2D::c2abs(data_fft, data_fft_f); 294 | } 295 | 296 | // Deal with situation when process is finished (redraw==false) but not fully rendered 297 | // Should also render at end of a full cycle but not when it's finished completely 298 | template 299 | bool ImGuiImageWindow::detect_frame_switch(int &fr_count) 300 | { 301 | int n_im = (fr_count) / nxy; 302 | if (last_img < n_im) 303 | { 304 | last_img = n_im; 305 | fr_count = nxy; 306 | this->last_y = 0; 307 | return true; 308 | } 309 | else 310 | { 311 | fr_count -= (n_im * nxy); 312 | return false; 313 | } 314 | } 315 | 316 | template <> 317 | void ImGuiImageWindow::value_tooltip(const int x, const int y, const float zoom) 318 | { 319 | float val = 0.0f; 320 | if (b_data_set) 321 | val = data->at(y * nx + x); 322 | ImGui::BeginTooltip(); 323 | ImGui::Text("XY: %i, %i", x, y); 324 | ImGui::Text("Value: %.2f", val); 325 | ImGui::Text("Zoom: %.2f", zoom); 326 | ImGui::EndTooltip(); 327 | } 328 | 329 | template <> 330 | void ImGuiImageWindow>::value_tooltip(const int x, const int y, const float zoom) 331 | { 332 | std::complex val = 0.0; 333 | if (b_data_set) 334 | val = data->at(y * nx + x); 335 | ImGui::BeginTooltip(); 336 | ImGui::Text("XY: %i, %i", x, y); 337 | ImGui::Text("Angle: %.2f", arg(val)); 338 | ImGui::Text("Magnitude: %.2f", abs(val)); 339 | ImGui::Text("Zoom: %.2f", zoom); 340 | ImGui::EndTooltip(); 341 | } 342 | 343 | // b_redraw is the standard timer based update, b_trigger_ext can trigger a full redraw of the image 344 | template 345 | void ImGuiImageWindow::render_window(bool b_redraw, int fr_count, bool b_trigger_ext) 346 | { 347 | ImGui::SetNextWindowSize(ImVec2{256, 256}, ImGuiCond_FirstUseEver); 348 | bool t_open = ImGui::Begin(title.c_str(), pb_open, ImGuiWindowFlags_NoScrollbar); 349 | if (t_open) 350 | { 351 | bool fr_switch = detect_frame_switch(fr_count); 352 | b_trigger_update = b_trigger_update || b_trigger_ext || fr_switch; 353 | if (b_trigger_update) 354 | { 355 | render_image(); 356 | } 357 | 358 | if (this->has(GIM_Flags::SaveImButton)) 359 | { 360 | ImGui::SameLine(); 361 | if (ImGui::Button("Save Image as...")) 362 | { 363 | saveFileDialog.Open(); 364 | } 365 | saveFileDialog.Display(); 366 | if (saveFileDialog.HasSelected()) 367 | { 368 | std::string img_file = saveFileDialog.GetSelected().string(); 369 | saveFileDialog.ClearSelected(); 370 | save_image(&img_file, sdl_srf); 371 | } 372 | } 373 | 374 | if (this->has(GIM_Flags::SaveDataButton)) 375 | { 376 | ImGui::SameLine(); 377 | if (ImGui::Button("Save Data as...")) 378 | { 379 | saveDataDialog.Open(); 380 | } 381 | saveDataDialog.Display(); 382 | if (saveDataDialog.HasSelected()) 383 | { 384 | std::string com_file = saveDataDialog.GetSelected().string(); 385 | saveDataDialog.ClearSelected(); 386 | save_numpy(&com_file, nx, ny, data); 387 | } 388 | } 389 | 390 | if (this->has(GIM_Flags::FftButton)) 391 | { 392 | ImGui::SameLine(); 393 | bool fft_button_press = ImGui::Button("Compute FFT"); 394 | if (fft_button_press) 395 | { 396 | *fft_window->pb_open = true; 397 | } 398 | if (fft_button_press || (*fft_window->pb_open && b_trigger_update)) 399 | { 400 | if (b_data_set) 401 | { 402 | compute_fft(); 403 | fft_window->set_data(nx, ny, &data_fft_f); 404 | fft_window->b_trigger_update = true; 405 | } 406 | else 407 | { 408 | std::cout << "FFT was not performed, because no data was found in " + this->title + "!" << std::endl; 409 | } 410 | } 411 | } 412 | 413 | if (this->has(GIM_Flags::PowerSlider)) 414 | { 415 | ImGui::SameLine(); 416 | ImGui::SetNextItemWidth(64); 417 | if (ImGui::DragFloat("Power", &power, 0.05f, 0.05f, 2.0f, "%.2f")) 418 | { 419 | this->last_idr = 0; 420 | this->last_y = 0; 421 | render_image((fr_count == 0) ? nxy : fr_count); 422 | b_trigger_update = true; 423 | } 424 | } 425 | 426 | if (this->has(GIM_Flags::ColormapSelector)) 427 | { 428 | ImGui::SameLine(); 429 | ImGui::SetNextItemWidth(-1); 430 | if (ImGui::Combo("Colormap", &data_cmap, cmaps, IM_ARRAYSIZE(cmaps))) 431 | { 432 | this->last_idr = 0; 433 | this->last_y = 0; 434 | render_image((fr_count == 0) ? nxy : fr_count); 435 | b_trigger_update = true; 436 | } 437 | } 438 | 439 | if (b_redraw && auto_render && fr_count > 0) 440 | render_image(fr_count); 441 | 442 | ImGui::SetNextWindowBgAlpha(0.0f); 443 | ImGui::BeginChildFrame(ImGui::GetID("ImageFrame"), ImVec2(0.0f, 0.0f), ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); 444 | ImVec2 vAvail = ImGui::GetContentRegionAvail(); 445 | float scale = (std::min)(vAvail.x / sdl_srf->w, vAvail.y / sdl_srf->h); 446 | float tex_h = sdl_srf->h * scale; 447 | float tex_w = sdl_srf->w * scale; 448 | float tex_h_z = tex_h * zoom; 449 | float tex_w_z = tex_w * zoom; 450 | if (b_redraw || b_trigger_update) 451 | { 452 | glBindTexture(GL_TEXTURE_2D, (*tex_id)); 453 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, sdl_srf->w, sdl_srf->h, 0, 454 | GL_BGRA, GL_UNSIGNED_BYTE, sdl_srf->pixels); 455 | } 456 | ImVec2 pos = ImGui::GetCursorScreenPos(); 457 | ImGui::Image((ImTextureID)(*tex_id), ImVec2(tex_w_z, tex_h_z), uv_min, uv_max, tint_col, border_col); 458 | if (ImGui::IsItemHovered()) 459 | { 460 | 461 | // Get Mouse Inputs 462 | float dz = (float)io.MouseWheel; 463 | ImVec2 xy = ImGui::GetMousePos(); 464 | 465 | // Compute relative cursor positions 466 | float rel_x = xy.x - pos.x - ImGui::GetScrollX(); 467 | float rel_y = xy.y - pos.y - ImGui::GetScrollY(); 468 | 469 | // Adjust Scroll positions 470 | // Capture Start position of scroll or drag/pan 471 | if ((std::abs(dz) > 0.0f) || ImGui::IsMouseClicked(ImGuiMouseButton_Left)) 472 | { 473 | start_x = rel_x; 474 | start_y = rel_y; 475 | start_xs = ImGui::GetScrollX(); 476 | start_ys = ImGui::GetScrollY(); 477 | } 478 | 479 | // Panning 480 | if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) 481 | { 482 | ImGui::SetScrollX(start_xs - (rel_x - start_x)); 483 | ImGui::SetScrollY(start_ys - (rel_y - start_y)); 484 | } 485 | 486 | // Zooming 487 | if (std::abs(dz) > 0.0f) 488 | { 489 | 490 | float zoom2 = zoom + dz * 0.1; 491 | zoom2 = (std::max)(1.0f, zoom2); 492 | 493 | float dx = ((xy.x - pos.x) / tex_w_z) * tex_w * (zoom2 - zoom); 494 | float dy = ((xy.y - pos.y) / tex_h_z) * tex_h * (zoom2 - zoom); 495 | 496 | ImGui::SetScrollX(start_xs + dx); 497 | ImGui::SetScrollY(start_ys + dy); 498 | 499 | zoom = zoom2; 500 | } 501 | 502 | // Value Popup 503 | if (ImGui::IsMouseDown(ImGuiMouseButton_Right)) 504 | { 505 | float scale_fct = scale * zoom; 506 | int x = (int)std::floor((xy.x - pos.x) / scale_fct); 507 | int y = (int)std::floor((xy.y - pos.y) / scale_fct); 508 | value_tooltip(x, y, zoom); 509 | } 510 | if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) 511 | { 512 | zoom = 1.0f; 513 | ImGui::SetScrollX(0.0f); 514 | ImGui::SetScrollY(0.0f); 515 | } 516 | } 517 | ImGui::EndChildFrame(); 518 | } 519 | ImGui::End(); 520 | b_trigger_update = false; 521 | } 522 | 523 | template class ImGuiImageWindow; 524 | template class ImGuiImageWindow>; -------------------------------------------------------------------------------- /src/ImGuiImageWindow.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "tinycolormap.hpp" 22 | #include "fft2d.hpp" 23 | 24 | #include "imgui.h" 25 | #include "imgui_impl_sdl.h" 26 | #include "imgui_impl_opengl3.h" 27 | #include "imgui_stdlib.h" 28 | #include "imgui_internal.h" 29 | #include "imfilebrowser.h" 30 | 31 | #include "GuiUtils.h" 32 | 33 | enum class GIM_Flags : unsigned char 34 | { 35 | None = 0, 36 | SaveImButton = 1 << 1, 37 | SaveDataButton = 1 << 2, 38 | ColormapSelector = 1 << 3, 39 | FftButton = 1 << 4, 40 | PowerSlider = 1 << 5, 41 | }; 42 | 43 | GIM_Flags operator&(GIM_Flags lhs, GIM_Flags rhs); 44 | GIM_Flags operator|(GIM_Flags lhs, GIM_Flags rhs); 45 | 46 | template 47 | class ImGuiImageWindow 48 | { 49 | public: 50 | ImGuiImageWindow(const std::string &title, GLuint *tex_id, bool auto_render, int data_cmap, GIM_Flags flags = GIM_Flags::None, bool *visible = nullptr); 51 | void set_data(int width, int height, std::vector *data); 52 | void render_window(bool b_redraw, int last_y, int render_update_offset, bool b_trigger_update); 53 | void render_window(bool b_redraw, int last_y, bool b_trigger_update); 54 | void reset_min_max(); 55 | void set_nx_ny(int width, int height); 56 | bool *pb_open; 57 | ImGuiImageWindow *fft_window; 58 | bool b_trigger_update; 59 | 60 | private: 61 | // Window properties 62 | std::string title; 63 | GIM_Flags flags; 64 | // ImVec2 pos; 65 | ImVec2 size; 66 | ImVec2 uv_min; 67 | ImVec2 uv_max; 68 | ImVec4 tint_col; 69 | ImVec4 border_col; 70 | 71 | // Interfaces 72 | ImGuiIO &io = ImGui::GetIO(); 73 | ImGui::FileBrowser saveFileDialog; 74 | ImGui::FileBrowser saveDataDialog; 75 | 76 | // Image properties 77 | float start_x, start_y; // Scrolling/Panning positions 78 | float start_xs, start_ys; // Scrolling/Panning positions 79 | const char *cmaps[13] = {"Parula", "Heat", "Jet", "Turbo", "Hot", "Gray", "Magma", "Inferno", "Plasma", "Viridis", "Cividis", "Github", "HSV"}; 80 | int data_cmap; 81 | float zoom; 82 | float power; 83 | bool auto_render; 84 | 85 | // Data Properties 86 | int nx; 87 | int ny; 88 | int nxy; 89 | int last_y; 90 | int last_idr; 91 | int last_img; 92 | int render_update_offset; // Accounts for ricom Kernel size 93 | float data_min; 94 | float data_max; 95 | float data_range; 96 | std::vector *data; 97 | bool b_data_set; 98 | 99 | // FFT data 100 | std::vector> data_fft; 101 | std::vector data_fft_f; 102 | std::vector> data_val; 103 | 104 | // Surface and Texture 105 | SDL_Surface *sdl_srf; 106 | GLuint *tex_id; 107 | 108 | // Methods 109 | inline void render_image(int ye); 110 | inline void render_image(); 111 | inline void set_min_max(); 112 | inline void set_min_max(int last_y); 113 | inline void reset_limits(); 114 | inline void set_pixel(int idx, int idy); 115 | inline void compute_fft(); 116 | inline bool has(GIM_Flags flag); 117 | inline bool detect_frame_switch(int &fr_count); 118 | inline float get_val(int idx); 119 | inline void value_tooltip(const int x, const int y, const float zoom); 120 | }; -------------------------------------------------------------------------------- /src/ProgressMonitor.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #include "ProgressMonitor.h" 15 | 16 | ProgressMonitor::ProgressMonitor(size_t fr_total, bool b_bar, float report_interval, std::ostream &out) : fr_count(0), fr_count_i(0), fr_freq(0), 17 | report_set(false), report_set_public(false), 18 | first_frame(true), fr(0), fr_avg(0), fr_count_a(0), 19 | time_stamp(chc::high_resolution_clock::now()), unit("kHz"), 20 | unit_bar("#"), unit_space("-") 21 | { 22 | this->fr_total = fr_total; 23 | this->b_bar = b_bar; 24 | this->out = &out; 25 | this->report_interval = report_interval; 26 | } 27 | 28 | int ProgressMonitor::GetBarLength() 29 | { 30 | // get console width and according adjust the length of the progress bar 31 | int bar_length = static_cast((TERMINAL_WIDTH - CHARACTER_WIDTH_PERCENTAGE) / 2.); 32 | return bar_length; 33 | } 34 | 35 | void ProgressMonitor::ClearBarField() 36 | { 37 | for (int i = 0; i < TERMINAL_WIDTH; ++i) 38 | { 39 | *out << " "; 40 | } 41 | *out << "\r" << std::flush; 42 | } 43 | 44 | ProgressMonitor &ProgressMonitor::operator++() 45 | { 46 | fr_count_i++; 47 | fr_count++; 48 | auto mil_secs = chc::duration_cast(chc::high_resolution_clock::now() - time_stamp).count(); 49 | if (mil_secs > report_interval || fr_count >= fr_total) 50 | { 51 | fr = fr_count_i / mil_secs; 52 | fr_avg += fr; 53 | fr_count_a++; 54 | fr_freq = fr_avg / fr_count_a; 55 | Report(fr_count, fr_avg / fr_count_a); 56 | report_set = true; 57 | report_set_public = true; 58 | } 59 | return *this; 60 | } 61 | 62 | void ProgressMonitor::reset_flags() 63 | { 64 | fr_count_i = 0; 65 | report_set = false; 66 | time_stamp = chc::high_resolution_clock::now(); 67 | } 68 | 69 | void ProgressMonitor::Report(unsigned long idx, float print_val) 70 | { 71 | try 72 | { 73 | if (idx > fr_total) 74 | throw idx; 75 | 76 | if (b_bar) // Print out the Progressbar and Frequency 77 | { 78 | // calculate percentage of progress 79 | double progress_percent = idx * TOTAL_PERCENTAGE / fr_total; 80 | 81 | // calculate the size of the progress bar 82 | int bar_size = GetBarLength(); 83 | 84 | // calculate the percentage value of a unit bar 85 | double percent_per_unit_bar = TOTAL_PERCENTAGE / bar_size; 86 | 87 | // display progress bar 88 | *out << "\r" 89 | << "["; 90 | 91 | for (int bar_length = 0; bar_length <= bar_size - 1; ++bar_length) 92 | { 93 | if (bar_length * percent_per_unit_bar < progress_percent) 94 | { 95 | *out << unit_bar; 96 | } 97 | else 98 | { 99 | *out << unit_space; 100 | } 101 | } 102 | *out << "]" << std::setw(CHARACTER_WIDTH_PERCENTAGE + 1); 103 | *out << std::setprecision(2) << std::fixed << print_val << std::fixed << " " << unit << std::flush; 104 | } 105 | if (idx >= fr_total) 106 | { 107 | *out << " " << std::endl 108 | << std::flush; 109 | } 110 | } 111 | catch (unsigned long e) 112 | { 113 | ClearBarField(); 114 | std::cerr << "EXCEPTION: frame index (" << e << ") went out of bounds (fr_total = " << fr_total << ")." << std::endl 115 | << std::flush; 116 | } 117 | } -------------------------------------------------------------------------------- /src/ProgressMonitor.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifndef _PROGRESS_MONITOR_ 15 | #define _PROGRESS_MONITOR_ 16 | 17 | #ifndef _WIN32 18 | #include 19 | #endif 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace chc = std::chrono; 29 | typedef chc::duration float_ms; 30 | 31 | #define TOTAL_PERCENTAGE 100.0 32 | #define CHARACTER_WIDTH_PERCENTAGE 4 33 | #define TERMINAL_WIDTH 120 34 | class ProgressMonitor 35 | { 36 | 37 | public: 38 | const static int DEFAULT_WIDTH; 39 | 40 | std::atomic fr_count; // frame count 41 | std::atomic fr_count_i; // Frame count in interval 42 | float fr_freq; // frame frequency 43 | std::atomic report_set; // update flag (reset internally) 44 | std::atomic report_set_public; // update flag (reset externally) 45 | bool first_frame; // first frame flag 46 | ProgressMonitor &operator++(); 47 | void reset_flags(); 48 | explicit ProgressMonitor(size_t fr_total, bool b_bar = true, float report_interval = 250.0, std::ostream &out = std::cerr); 49 | 50 | private: 51 | bool b_bar; // Print progress bar 52 | size_t fr_total; // Total number of frames 53 | std::ostream *out; // Output stream 54 | 55 | float fr; // Frequncy per frame 56 | float fr_avg; // Average frequency 57 | float report_interval; // Update interval 58 | std::atomic fr_count_a; // Count measured points for averaging 59 | 60 | chc::time_point time_stamp; 61 | 62 | const char *unit; 63 | const char *unit_bar; 64 | const char *unit_space; 65 | 66 | void Report(unsigned long idx, float print_val); 67 | void ClearBarField(); 68 | int GetBarLength(); 69 | }; 70 | 71 | #endif // _PROGRESS_MONITOR_ 72 | -------------------------------------------------------------------------------- /src/Ricom.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifndef RICOM_H 15 | #define RICOM_H 16 | 17 | #ifdef _WIN32 18 | #include 19 | #pragma warning(disable : 4005 4333 34) 20 | #else 21 | #include 22 | #endif 23 | 24 | #define _USE_MATH_DEFINES 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "BoundedThreadPool.hpp" 41 | #include "tinycolormap.hpp" 42 | #include "fft2d.hpp" 43 | #include "SocketConnector.h" 44 | #include "ProgressMonitor.h" 45 | #include "MerlinInterface.h" 46 | #include "TimepixInterface.h" 47 | #include "Camera.h" 48 | #include "GuiUtils.h" 49 | 50 | namespace chc = std::chrono; 51 | 52 | class Ricom_kernel 53 | { 54 | public: 55 | // Properties 56 | int kernel_size; 57 | bool b_filter; 58 | std::array kernel_filter_frequency; 59 | int k_width_sym; 60 | int k_area; 61 | float rotation; 62 | std::vector kernel_x; 63 | std::vector kernel_y; 64 | std::vector kernel_filter; 65 | std::vector f_approx; 66 | SDL_Surface *srf_kx; 67 | SDL_Surface *srf_ky; 68 | // Methods 69 | void compute_kernel(); 70 | void compute_filter(); 71 | void include_filter(); 72 | std::vector fftshift_map(int x, int y); 73 | // Constructor 74 | Ricom_kernel() : kernel_size(5), 75 | b_filter(false), 76 | kernel_filter_frequency{1, 4}, 77 | k_width_sym(0), 78 | k_area(0), 79 | rotation(0.0), 80 | kernel_x(), 81 | kernel_y(), 82 | kernel_filter(), 83 | f_approx(), 84 | srf_kx(), srf_ky() 85 | { 86 | compute_kernel(); 87 | }; 88 | void approximate_frequencies(size_t n_im); 89 | void draw_surfaces(); 90 | // Destructor 91 | ~Ricom_kernel(){}; 92 | }; 93 | 94 | //////////////////////////////////////////////// 95 | // Helper class for ricom data indexing // 96 | //////////////////////////////////////////////// 97 | class id_x_y 98 | { 99 | public: 100 | int id; 101 | bool valid; 102 | id_x_y() : id(0), valid(false){}; 103 | id_x_y(int id, bool valid); 104 | }; 105 | 106 | class Update_list 107 | { 108 | private: 109 | // Properties 110 | Ricom_kernel kernel; 111 | int nx; 112 | int ny; 113 | 114 | public: 115 | // Properties 116 | std::vector ids; 117 | // Methods 118 | void init(Ricom_kernel kernel, int nx_ricom, int ny_ricom); 119 | inline void shift(id_x_y &id_sft, int id, int shift); 120 | // Constructor 121 | Update_list() : kernel(), nx(0), ny(0){}; 122 | }; 123 | 124 | class Ricom_detector 125 | { 126 | public: 127 | // Properties 128 | std::array radius; 129 | std::array radius2; 130 | std::vector id_list; 131 | 132 | // Methods 133 | void compute_detector(int nx_cam, int ny_cam, std::array &offset); 134 | // Constructor 135 | Ricom_detector() : radius{0, 0}, radius2{0, 0}, id_list(){}; 136 | // Destructor 137 | ~Ricom_detector(){}; 138 | }; 139 | 140 | namespace RICOM 141 | { 142 | enum modes 143 | { 144 | FILE, 145 | TCP 146 | }; 147 | void run_ricom(Ricom *r, RICOM::modes mode); 148 | void run_connection_script(Ricom *r, MerlinSettings *merlin, const std::string &python_path); 149 | } 150 | 151 | class Ricom 152 | { 153 | private: 154 | // vSTEM Variables 155 | float stem_max; 156 | float stem_min; 157 | 158 | // ricom variables 159 | std::vector u; 160 | std::vector v; 161 | 162 | Update_list update_list; 163 | 164 | // Electric field magnitude 165 | float e_mag_max; 166 | float e_mag_min; 167 | 168 | // Variables for potting in the SDL2 frame 169 | float ricom_max; 170 | float ricom_min; 171 | std::vector cbed_log; 172 | 173 | // Thread Synchronization Variables 174 | std::mutex ricom_mutex; 175 | std::mutex stem_mutex; 176 | std::mutex counter_mutex; 177 | std::mutex e_field_mutex; 178 | 179 | // Private Methods - General 180 | void init_surface(); 181 | template 182 | inline void update_surfaces(int iy, std::vector *p_frame); 183 | void reinit_vectors_limits(); 184 | void reset_limits(); 185 | void reset_file(); 186 | void calculate_update_list(); 187 | inline void rescales_recomputes(); 188 | template 189 | inline void skip_frames(int n_skip, std::vector &data, CAMERA::Camera *camera_fr); 190 | template 191 | inline void swap_endianess(T &val); 192 | 193 | // Private Methods - riCOM 194 | inline void icom(std::array *com, int x, int y); 195 | inline void icom(std::array com, int x, int y); 196 | template 197 | inline void com(std::vector *data, std::array &com); 198 | template 199 | void read_com_merlin(std::vector &data, std::array &com); 200 | inline void set_ricom_pixel(int idx, int idy); 201 | template 202 | inline void com_icom(std::vector data, int ix, int iy, std::array *com_xy_sum, ProgressMonitor *p_prog_mon); 203 | template 204 | inline void com_icom(std::vector *p_data, int ix, int iy, std::array *com_xy_sum, ProgressMonitor *p_prog_mon); 205 | 206 | // Private Methods - vSTEM 207 | template 208 | inline void stem(std::vector *data, size_t id_stem); 209 | inline void set_stem_pixel(size_t idx, size_t idy); 210 | 211 | // Private Methods electric field 212 | inline void compute_electric_field(std::array &p_com_xy, size_t id); 213 | inline void set_e_field_pixel(size_t idx, size_t idy); 214 | 215 | public: 216 | SocketConnector socket; 217 | std::string file_path; 218 | CAMERA::Camera_BASE camera; 219 | RICOM::modes mode; 220 | bool b_print2file; 221 | int redraw_interval; 222 | int last_y; 223 | ProgressMonitor *p_prog_mon; 224 | bool b_busy; 225 | bool update_offset; 226 | bool b_vSTEM; 227 | bool b_e_mag; 228 | bool b_plot_cbed; 229 | bool b_plot2SDL; 230 | bool b_recompute_detector; 231 | bool b_recompute_kernel; 232 | Ricom_detector detector; 233 | Ricom_kernel kernel; 234 | std::array offset; 235 | std::array com_public; 236 | std::vector com_map_x; 237 | std::vector com_map_y; 238 | std::vector ricom_data; 239 | std::vector stem_data; 240 | std::vector> e_field_data; 241 | 242 | // Scan Area Variables 243 | int nx; 244 | int ny; 245 | int nxy; 246 | int rep; 247 | int fr_total; 248 | int skip_row; 249 | int skip_img; 250 | 251 | // Variables for progress and performance 252 | int n_threads; 253 | int n_threads_max; 254 | int queue_size; 255 | float fr_freq; // Frequncy per frame 256 | float fr_count; // Count all Frames processed in an image 257 | float fr_count_total; // Count all Frames in a scanning session 258 | std::atomic rescale_ricom; 259 | std::atomic rescale_stem; 260 | std::atomic rescale_e_mag; 261 | bool rc_quit; 262 | 263 | SDL_Surface *srf_ricom; // Surface for the ricom window; 264 | int ricom_cmap; 265 | SDL_Surface *srf_stem; // Surface for the vSTEM window; 266 | int stem_cmap; 267 | SDL_Surface *srf_cbed; // Surface for the CBED window; 268 | int cbed_cmap; 269 | SDL_Surface *srf_e_mag; // Surface for the E-Field window; 270 | int e_mag_cmap; 271 | 272 | // Public Methods 273 | void draw_ricom_image(); 274 | void draw_ricom_image(int y0, int ye); 275 | void draw_stem_image(); 276 | void draw_stem_image(int y0, int ye); 277 | void draw_e_field_image(); 278 | void draw_e_field_image(int y0, int ye); 279 | template 280 | void run_reconstruction(RICOM::modes mode); 281 | void reset(); 282 | template 283 | void plot_cbed(std::vector *p_data); 284 | template 285 | void process_data(CAMERA::Camera *camera); 286 | template 287 | void process_data(CAMERA::Camera *camera); 288 | enum CAMERA::Camera_model select_mode_by_file(const char *filename); 289 | 290 | // Constructor 291 | Ricom(); 292 | 293 | // Destructor 294 | ~Ricom(); 295 | }; 296 | 297 | #endif // __RICOM_H__ -------------------------------------------------------------------------------- /src/RunCLI.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | #include "Ricom.h" 18 | #include "SdlImageWindow.h" 19 | #include "GuiUtils.h" 20 | 21 | int run_cli(int argc, char *argv[], Ricom *ricom, CAMERA::Default_configurations &hardware_configurations) 22 | { 23 | ricom->b_plot_cbed = false; 24 | std::string save_img = ""; 25 | std::string save_dat = ""; 26 | 27 | // command line arguments 28 | for (int i = 1; i < argc; i++) 29 | { 30 | if (i + 1 != argc) 31 | { 32 | // Set filename to read from .mib file 33 | if (strcmp(argv[i], "-filename") == 0) 34 | { 35 | ricom->camera = hardware_configurations[ricom->select_mode_by_file(argv[i + 1])]; 36 | i++; 37 | } 38 | // Set IP of camera for TCP connection 39 | if (strcmp(argv[i], "-ip") == 0) 40 | { 41 | ricom->socket.ip = argv[i + 1]; 42 | ricom->mode = RICOM::TCP; 43 | i++; 44 | } 45 | // Set port data-read port of camera 46 | if (strcmp(argv[i], "-port") == 0) 47 | { 48 | ricom->socket.port = std::stoi(argv[i + 1]); 49 | ricom->mode = RICOM::TCP; 50 | i++; 51 | } 52 | // Set width of image 53 | if (strcmp(argv[i], "-nx") == 0) 54 | { 55 | ricom->nx = std::stoi(argv[i + 1]); 56 | i++; 57 | } 58 | // Set height of image 59 | if (strcmp(argv[i], "-ny") == 0) 60 | { 61 | ricom->ny = std::stoi(argv[i + 1]); 62 | i++; 63 | } 64 | // Set width of camera 65 | if (strcmp(argv[i], "-cam_nx") == 0) 66 | { 67 | ricom->camera.nx_cam = std::stoi(argv[i + 1]); 68 | ricom->offset[0] = ((float)ricom->camera.nx_cam - 1) / 2; 69 | i++; 70 | } 71 | // Set height of camera 72 | if (strcmp(argv[i], "-cam_ny") == 0) 73 | { 74 | ricom->camera.ny_cam = std::stoi(argv[i + 1]); 75 | ricom->offset[1] = ((float)ricom->camera.ny_cam - 1) / 2; 76 | i++; 77 | } 78 | // Set skip per row 79 | if (strcmp(argv[i], "-skipr") == 0) 80 | { 81 | ricom->skip_row = std::stoi(argv[i + 1]); 82 | i++; 83 | } 84 | // Set skip per image 85 | if (strcmp(argv[i], "-skipi") == 0) 86 | { 87 | ricom->skip_img = std::stoi(argv[i + 1]); 88 | i++; 89 | } 90 | // Set kernel size 91 | if (strcmp(argv[i], "-k") == 0) 92 | { 93 | ricom->kernel.kernel_size = std::stoi(argv[i + 1]); 94 | i++; 95 | } 96 | // Set CBED Rotation 97 | if (strcmp(argv[i], "-r") == 0) 98 | { 99 | ricom->kernel.rotation = std::stof(argv[i + 1]); 100 | i++; 101 | } 102 | // Set CBED center offset 103 | if (strcmp(argv[i], "-offset") == 0) 104 | { 105 | ricom->offset[0] = std::stof(argv[i + 1]); 106 | i++; 107 | ricom->offset[1] = std::stof(argv[i + 1]); 108 | i++; 109 | } 110 | // Set CBED center offset 111 | if (strcmp(argv[i], "-update_offset") == 0) 112 | { 113 | ricom->update_offset = (bool)std::stoi(argv[i + 1]); 114 | i++; 115 | } 116 | // Set STEM radii 117 | if (strcmp(argv[i], "-radius") == 0) 118 | { 119 | ricom->b_vSTEM = true; 120 | ricom->detector.radius[0] = std::stof(argv[i + 1]); 121 | i++; 122 | ricom->detector.radius[1] = std::stof(argv[i + 1]); 123 | i++; 124 | } 125 | // Set kernel filter 126 | if (strcmp(argv[i], "-f") == 0) 127 | { 128 | ricom->kernel.b_filter = true; 129 | ricom->kernel.kernel_filter_frequency[0] = std::stoi(argv[i + 1]); 130 | i++; 131 | ricom->kernel.kernel_filter_frequency[1] = std::stoi(argv[i + 1]); 132 | i++; 133 | } 134 | // Set depth of pixel for raw mode 135 | if (strcmp(argv[i], "-depth") == 0) 136 | { 137 | ricom->camera.depth = std::stoi(argv[i + 1]); 138 | ricom->camera.model = CAMERA::MERLIN; 139 | i++; 140 | } 141 | // Set number of repetitions 142 | if (strcmp(argv[i], "-rep") == 0) 143 | { 144 | ricom->rep = std::stoi(argv[i + 1]); 145 | i++; 146 | } 147 | // Set Dwell Time 148 | if (strcmp(argv[i], "-dwell_time") == 0) 149 | { 150 | ricom->camera.dwell_time = std::stof(argv[i + 1]); 151 | ricom->camera.model = CAMERA::TIMEPIX; 152 | i++; 153 | } 154 | // Set Number of threads 155 | if (strcmp(argv[i], "-threads") == 0) 156 | { 157 | ricom->n_threads = std::stoi(argv[i + 1]); 158 | i++; 159 | } 160 | // Set Number queue size 161 | if (strcmp(argv[i], "-queue_size") == 0) 162 | { 163 | ricom->queue_size = std::stoi(argv[i + 1]); 164 | i++; 165 | } 166 | // Set redraw interval in ms 167 | if (strcmp(argv[i], "-redraw_interval") == 0) 168 | { 169 | ricom->redraw_interval = std::stoi(argv[i + 1]); 170 | ricom->b_plot2SDL = true; 171 | i++; 172 | } 173 | // Set path to save reconstruction image 174 | if (strcmp(argv[i], "-save_img_path") == 0) 175 | { 176 | save_img = argv[i + 1]; 177 | ricom->b_plot2SDL = true; 178 | i++; 179 | } 180 | // Set path to save reconstruction data 181 | if (strcmp(argv[i], "-save_data_path") == 0) 182 | { 183 | save_dat = argv[i + 1]; 184 | i++; 185 | } 186 | // plot electric field 187 | if (strcmp(argv[i], "-plot_e_field") == 0) 188 | { 189 | ricom->b_e_mag = (bool)std::stoi(argv[i + 1]); 190 | i++; 191 | } 192 | } 193 | } 194 | 195 | if (ricom->b_plot2SDL) 196 | { 197 | std::vector image_windows; 198 | std::thread run_thread; 199 | run_thread = std::thread(RICOM::run_ricom, ricom, ricom->mode); 200 | while (ricom->srf_ricom == NULL) 201 | { 202 | SDL_Delay(ricom->redraw_interval); 203 | } 204 | 205 | run_thread.detach(); 206 | 207 | // // Initializing SDL 208 | SDL_DisplayMode DM; // To get the current display size 209 | SDL_Event event; // Event variable 210 | 211 | SDL_Init(SDL_INIT_EVERYTHING); 212 | SDL_GetCurrentDisplayMode(0, &DM); 213 | float scale = (std::min)(((float)DM.w) / ricom->nx, ((float)DM.h) / ricom->ny) * 0.8; 214 | bool b_redraw = false; 215 | image_windows.push_back(SdlImageWindow("riCOM", ricom->srf_ricom, ricom->nx, ricom->ny, scale)); 216 | if (ricom->b_vSTEM) 217 | { 218 | image_windows.push_back(SdlImageWindow("vSTEM", ricom->srf_stem, ricom->nx, ricom->ny, scale)); 219 | } 220 | if (ricom->b_e_mag) 221 | { 222 | image_windows.push_back(SdlImageWindow("E-Field", ricom->srf_e_mag, ricom->nx, ricom->ny, scale)); 223 | } 224 | 225 | bool b_open_window = true; 226 | while (b_open_window) 227 | { 228 | if (ricom->p_prog_mon != nullptr) 229 | { 230 | if (ricom->p_prog_mon->report_set_public) 231 | { 232 | b_redraw = true; 233 | ricom->p_prog_mon->report_set_public = false; 234 | } 235 | else 236 | { 237 | b_redraw = true; 238 | } 239 | } 240 | 241 | if (b_redraw) 242 | { 243 | for (auto &wnd : image_windows) 244 | { 245 | wnd.update_image(); 246 | } 247 | } 248 | 249 | while (SDL_PollEvent(&event)) 250 | { 251 | if (event.type == SDL_QUIT || 252 | (event.type == SDL_WINDOWEVENT && 253 | event.window.event == SDL_WINDOWEVENT_CLOSE)) 254 | { 255 | ricom->rc_quit = true; 256 | SDL_Delay(ricom->redraw_interval); 257 | b_open_window = false; 258 | } 259 | } 260 | } 261 | } 262 | else 263 | { 264 | RICOM::run_ricom(ricom, ricom->mode); 265 | } 266 | if (save_dat != "") 267 | { 268 | save_numpy(&save_dat, ricom->nx, ricom->ny, &ricom->ricom_data); 269 | std::cout << "riCOM reconstruction data saved as " + save_dat << std::endl; 270 | } 271 | if (save_img != "") 272 | { 273 | save_image(&save_img, ricom->srf_ricom); 274 | std::cout << "riCOM reconstruction image saved as " + save_img << std::endl; 275 | } 276 | return 0; 277 | } -------------------------------------------------------------------------------- /src/SdlImageWindow.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #include "SdlImageWindow.h" 15 | 16 | SdlImageWindow::SdlImageWindow(const char *title, SDL_Surface *srf, int nx, int ny, float scale) 17 | { 18 | this->title = title; 19 | create_window(srf, nx, ny, scale); 20 | }; 21 | 22 | SdlImageWindow::SdlImageWindow(const char *title) 23 | { 24 | this->title = title; 25 | this->window = NULL; // Pointer for the window 26 | this->renderer = NULL; // Pointer for the renderer 27 | this->tex = NULL; // Texture for the window 28 | this->srf = NULL; // Surface (data source) 29 | }; 30 | 31 | void SdlImageWindow::create_window(SDL_Surface *srf, int nx, int ny, float scale) 32 | { 33 | // Creating window 34 | window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, scale * nx, scale * ny, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); 35 | if (window == NULL) 36 | { 37 | std::cout << "Window could not be created! SDL Error: " << SDL_GetError() << std::endl; 38 | } 39 | // Creating Renderer 40 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); 41 | if (renderer == NULL) 42 | { 43 | std::cout << "Renderer could not be created! SDL Error: " << SDL_GetError() << std::endl; 44 | } 45 | // Creating texture for hardware rendering 46 | tex = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), SDL_TEXTUREACCESS_STATIC, nx, ny); 47 | if (tex == NULL) 48 | { 49 | std::cout << "Texture could not be created! SDL Error: " << SDL_GetError() << std::endl; 50 | } 51 | // Maintain Pixel aspect ratio on resizing 52 | if (SDL_RenderSetLogicalSize(renderer, scale * nx, scale * ny) < 0) 53 | { 54 | std::cout << "Logical size could not be set! SDL Error: " << SDL_GetError() << std::endl; 55 | } 56 | this->srf = srf; 57 | } 58 | 59 | void SdlImageWindow::update_image() 60 | { 61 | if (srf != NULL) 62 | { 63 | if (SDL_UpdateTexture(tex, NULL, srf->pixels, srf->pitch) == -1) 64 | { 65 | std::cout << "SDL_UpdateTexture failed: " << SDL_GetError() << std::endl; 66 | } 67 | if (SDL_RenderCopy(renderer, tex, NULL, NULL) == -1) 68 | { 69 | std::cout << "SDL_RenderCopy failed: " << SDL_GetError() << std::endl; 70 | } 71 | SDL_RenderPresent(renderer); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/SdlImageWindow.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifndef SDLIMAGEWINDOW_H 15 | #define SDLIMAGEWINDOW_H 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | class SdlImageWindow 23 | { 24 | public: 25 | const char *title; // Window title 26 | explicit SdlImageWindow(const char *title); 27 | explicit SdlImageWindow(const char *title, SDL_Surface *srf, int nx, int ny, float scale); 28 | void create_window(SDL_Surface *srf, int nx, int ny, float scale); 29 | void update_image(); 30 | 31 | private: 32 | SDL_Window *window; // Pointer for the window 33 | SDL_Renderer *renderer; // Pointer for the renderer 34 | SDL_Texture *tex; // Texture for the window 35 | SDL_Surface *srf; // Surface (data source) 36 | }; 37 | #endif // SDLIMAGEWINDOW_H -------------------------------------------------------------------------------- /src/SocketConnector.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #include "SocketConnector.h" 15 | 16 | int SocketConnector::read_data(char *buffer, int data_size) 17 | { 18 | int bytes_payload_total = 0; 19 | 20 | while (bytes_payload_total < data_size) 21 | { 22 | int bytes_payload_count = recv(rc_socket, 23 | &buffer[bytes_payload_total], 24 | data_size - bytes_payload_total, 25 | 0); 26 | 27 | if (bytes_payload_count == -1) 28 | { 29 | perror("Error reading Data!"); 30 | return -1; 31 | } 32 | else if (bytes_payload_count == 0) 33 | { 34 | std::cout << "Unexpected end of transmission" << std::endl; 35 | return -1; 36 | } 37 | bytes_payload_total += bytes_payload_count; 38 | } 39 | return 0; 40 | } 41 | 42 | void SocketConnector::flush_socket() 43 | { 44 | close_socket(); 45 | connect_socket(); 46 | char *buffer = {0}; 47 | int bytes_total = 0; 48 | 49 | while (true) 50 | { 51 | int bytes_count = recv(rc_socket, &buffer[0], 1, 0); 52 | 53 | if (bytes_count <= 0) 54 | { 55 | std::cout << "Socket flushed (" << bytes_total / 1024 << " kB)" << std::endl; 56 | break; 57 | } 58 | bytes_total += bytes_count; 59 | } 60 | close_socket(); 61 | } 62 | 63 | void SocketConnector::connect_socket() 64 | { 65 | #ifdef WIN32 66 | int error = WSAStartup(0x0202, &w); 67 | if (error) 68 | { 69 | exit(EXIT_FAILURE); 70 | } 71 | #endif 72 | 73 | // Creating socket file descriptor 74 | rc_socket = socket(AF_INET, SOCK_STREAM, 0); 75 | if (rc_socket == INVALID_SOCKET) 76 | { 77 | handle_socket_errors("intitializing Socket"); 78 | } 79 | 80 | if (setsockopt(rc_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == SOCKET_ERROR) 81 | { 82 | handle_socket_errors("setting socket options"); 83 | } 84 | 85 | address.sin_family = AF_INET; 86 | address.sin_addr.s_addr = inet_addr(ip.c_str()); 87 | address.sin_port = htons(port); 88 | 89 | std::cout << "Waiting for incoming connection..." << std::endl; 90 | 91 | // Connecting socket to the port 92 | int error_counter = 0; 93 | while (true) 94 | { 95 | if (connect(rc_socket, (struct sockaddr *)&address, sizeof(address)) == SOCKET_ERROR) 96 | { 97 | if (error_counter < 1) 98 | { 99 | handle_socket_errors("connecting to Socket"); 100 | } 101 | error_counter++; 102 | } 103 | else 104 | { 105 | std::cout << "Connected by " << inet_ntoa(address.sin_addr) << "\n"; 106 | b_connected = true; 107 | break; 108 | } 109 | } 110 | } 111 | 112 | #ifdef WIN32 113 | void SocketConnector::handle_socket_errors(const std::string &raised_at) 114 | { 115 | wchar_t *s = NULL; 116 | FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 117 | NULL, WSAGetLastError(), 118 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 119 | (LPWSTR)&s, 0, NULL); 120 | std::cout << "Error occured while " << raised_at << "." << std::endl; 121 | fprintf(stderr, "%S\n", s); 122 | LocalFree(s); 123 | } 124 | 125 | void SocketConnector::close_socket() 126 | { 127 | closesocket(rc_socket); 128 | WSACleanup(); 129 | } 130 | #else 131 | void SocketConnector::handle_socket_errors(const std::string &raised_at) 132 | { 133 | std::cout << "Error occured while " << raised_at << "." << std::endl; 134 | std::cout << std::strerror(errno) << std::endl; 135 | } 136 | 137 | void SocketConnector::close_socket() 138 | { 139 | close(rc_socket); 140 | } 141 | #endif 142 | -------------------------------------------------------------------------------- /src/SocketConnector.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifndef SOCKET_CONNECTOR_H 15 | #define SOCKET_CONNECTOR_H 16 | 17 | #ifdef _WIN32 18 | #pragma comment(lib, "ws2_32.lib") 19 | #define _WINSOCK_DEPRECATED_NO_WARNINGS 20 | #include 21 | #include 22 | #include 23 | #else 24 | #include 25 | #include 26 | #include 27 | #include 28 | #define SOCKET int 29 | #define SOCKET_ERROR -1 30 | #define INVALID_SOCKET -1 31 | #endif 32 | 33 | #include 34 | #include 35 | 36 | class SocketConnector 37 | { 38 | public: 39 | SOCKET rc_socket; 40 | 41 | bool b_connected; 42 | std::string ip; 43 | int port; 44 | 45 | std::string connection_information; 46 | int read_data(char *buffer, int data_size); 47 | void flush_socket(); 48 | void connect_socket(); 49 | void close_socket(); 50 | SocketConnector() : rc_socket(INVALID_SOCKET), 51 | b_connected(false), 52 | ip("127.0.0.1"), 53 | port(6342){}; 54 | 55 | private: 56 | struct sockaddr_in address; 57 | #ifdef WIN32 58 | WSADATA w; 59 | const char opt = 1; 60 | #else 61 | int opt = 1; 62 | #endif 63 | void handle_socket_errors(const std::string &raised_at); 64 | }; 65 | #endif // SOCKET_CONNECTOR_H -------------------------------------------------------------------------------- /src/cameras/MerlinInterface.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #include "MerlinInterface.h" 15 | 16 | void MerlinInterface::read_head_data() 17 | { 18 | switch (mode) 19 | { 20 | case MODE_FILE: 21 | file.read_data(&head_buffer[0], head_buffer.size()); 22 | break; 23 | case MODE_TCP: 24 | if (socket->read_data(&tcp_buffer[0], tcp_buffer.size()) == -1) 25 | { 26 | perror("MerlinInterface::read_head_data(): Error reading TCP header from Socket!"); 27 | } 28 | if (socket->read_data(&head_buffer[0], head_buffer.size()) == -1) 29 | { 30 | perror("MerlinInterface::read_head_data(): Error reading Frame header from Socket!"); 31 | } 32 | break; 33 | } 34 | } 35 | 36 | void MerlinInterface::read_data(char *buffer, int data_size) 37 | { 38 | switch (mode) 39 | { 40 | case MODE_FILE: 41 | file.read_data(buffer, data_size); 42 | break; 43 | case MODE_TCP: 44 | if (socket->read_data(buffer, data_size) == -1) 45 | { 46 | perror("MerlinInterface::read_data(): Error reading frame data from Socket!"); 47 | } 48 | break; 49 | } 50 | } 51 | 52 | void MerlinInterface::init_uv(std::vector &u, std::vector &v) 53 | { 54 | u.resize(nx); 55 | v.resize(ny); 56 | 57 | for (int i = 0; i < ny; i++) 58 | { 59 | v[i] = i; 60 | } 61 | if (b_raw) 62 | // raw format: pixel sequence flipped every 64 bit 63 | { 64 | size_t num_per_flip = 64; 65 | switch (data_depth) 66 | { 67 | case 1: 68 | num_per_flip = 64; 69 | break; 70 | case 6: 71 | num_per_flip = 8; 72 | break; 73 | case 12: 74 | num_per_flip = 4; 75 | break; 76 | } 77 | size_t cnt = 0; 78 | for (size_t i = 0; i < (nx / num_per_flip); i++) 79 | { 80 | for (size_t j = (num_per_flip * (i + 1)); j > num_per_flip * i; j--) 81 | { 82 | u[cnt] = (j - 1); 83 | cnt++; 84 | } 85 | } 86 | } 87 | else 88 | { 89 | for (int i = 0; i < nx; i++) 90 | { 91 | u[i] = i; 92 | } 93 | } 94 | } 95 | 96 | template 97 | void MerlinInterface::convert_binary_to_chars(std::vector &data) 98 | { 99 | size_t T_size = static_cast(sizeof(T) * 8); 100 | size_t i_dat = static_cast(data.size() / T_size); 101 | for (size_t i = i_dat - 1; i > 0; i--) 102 | { 103 | size_t idx = i * T_size; 104 | for (size_t j = 0; j < T_size; j++) 105 | { 106 | data[idx + j] = static_cast((data[i] >> j) & 1); 107 | } 108 | } 109 | } 110 | 111 | // Reading and decoding header data 112 | bool MerlinInterface::read_head(bool decode) 113 | { 114 | try 115 | { 116 | read_head_data(); 117 | if (decode) 118 | { 119 | decode_head(); 120 | } 121 | return true; 122 | } 123 | catch (const std::exception &e) 124 | { 125 | std::cerr << e.what() << '\n'; 126 | return false; 127 | } 128 | } 129 | 130 | int MerlinInterface::read_aquisition_header() 131 | { 132 | if (socket->read_data(&tcp_buffer[0], tcp_buffer.size()) == -1) 133 | { 134 | std::cout << "Error on recv() reading TCP-Header" << std::endl; 135 | return -1; 136 | } 137 | int l = decode_tcp_head(); 138 | acq_header.resize(l); 139 | char *buffer = reinterpret_cast(&acq_header[0]); 140 | socket->read_data(buffer, l); 141 | char *p = strtok(buffer, " "); 142 | while (p) 143 | { 144 | acq += std::string(p) + "\n"; 145 | std::cout << p << std::endl; // printing each token 146 | p = strtok(NULL, "\n"); 147 | } 148 | socket->connection_information = acq; 149 | return 0; 150 | } 151 | 152 | int MerlinInterface::decode_tcp_head() 153 | { 154 | rcv.assign(tcp_buffer.cbegin(), tcp_buffer.cend()); 155 | std::string n = rcv.substr(4, 10); 156 | 157 | try 158 | { 159 | int ds = stoi(n); 160 | return ds; 161 | } 162 | catch (const std::exception &e) 163 | { 164 | std::cerr << e.what() << '\n'; 165 | return 0; 166 | } 167 | } 168 | 169 | void MerlinInterface::decode_head() 170 | { 171 | rcv.assign(head_buffer.cbegin(), head_buffer.cend()); 172 | size_t i = 0; 173 | head.fill(""); 174 | std::stringstream ss(rcv); 175 | while (ss.good() && i <= 6) 176 | { 177 | std::getline(ss, head[i], ','); 178 | i++; 179 | } 180 | if (i >= 6) 181 | { 182 | try 183 | { 184 | nx = stoi(head[4]); 185 | ny = stoi(head[5]); 186 | ds_merlin = nx * ny; 187 | dtype = head[6]; 188 | } 189 | catch (const std::exception &e) 190 | { 191 | std::cerr << e.what() << '\n'; 192 | } 193 | } 194 | else 195 | { 196 | perror("Frame Header cannot be decoded!"); 197 | } 198 | } 199 | 200 | // Public Methods 201 | int MerlinInterface::pre_run(std::vector &u, std::vector &v) 202 | { 203 | if (mode == MODE_TCP) 204 | { 205 | if (read_aquisition_header() == -1) 206 | { 207 | perror("MerlinInterface::pre_run() could not obtain aquisition_header"); 208 | return -1; 209 | } 210 | } 211 | 212 | if (read_head()) 213 | { 214 | b_raw = false; 215 | b_binary = false; 216 | 217 | if (dtype == "U08") 218 | { 219 | return 8; 220 | } 221 | else if (dtype == "U16") 222 | { 223 | return 16; 224 | } 225 | else if (dtype == "R64") 226 | { 227 | b_raw = true; 228 | init_uv(u, v); 229 | switch (data_depth) 230 | { 231 | case 1: 232 | b_binary = true; 233 | return 8; 234 | case 6: 235 | return 16; 236 | case 12: 237 | return 16; 238 | default: 239 | perror("MerlinInterface::pre_run() supplied data_depth value not valid (must be 1, 6 or 12)."); 240 | return -1; 241 | } 242 | } 243 | else 244 | { 245 | perror("MerlinInterface::pre_run() could not find valid data type description in frame header"); 246 | return -1; 247 | } 248 | } 249 | else 250 | { 251 | perror("MerlinInterface::pre_run() could not read frame header"); 252 | return -1; 253 | } 254 | return -1; 255 | } 256 | 257 | template 258 | void MerlinInterface::read_frame(std::vector &data, bool dump_head) 259 | { 260 | if (dump_head) 261 | { 262 | read_head(false); 263 | } 264 | int data_size = static_cast(ds_merlin * sizeof(T)); 265 | char *buffer = reinterpret_cast(&data[0]); 266 | if (b_binary) 267 | { 268 | data_size /= 8; 269 | } 270 | 271 | read_data(buffer, data_size); 272 | 273 | if (b_binary) 274 | { 275 | convert_binary_to_chars(data); 276 | } 277 | }; 278 | 279 | // Template Specializations to avoid linker issues 280 | template void MerlinInterface::read_frame(std::vector &data, bool dump_head); 281 | template void MerlinInterface::read_frame(std::vector &data, bool dump_head); 282 | 283 | void MerlinInterface::init_interface(SocketConnector *socket) 284 | { 285 | mode = MODE_TCP; 286 | this->socket = socket; 287 | // socket->flush_socket(); 288 | socket->connect_socket(); 289 | }; 290 | 291 | void MerlinInterface::init_interface(const std::string &path) 292 | { 293 | mode = MODE_FILE; 294 | file.path = path; 295 | file.open_file(); 296 | }; 297 | 298 | void MerlinInterface::close_interface() 299 | { 300 | switch (mode) 301 | { 302 | case MODE_TCP: 303 | socket->close_socket(); 304 | socket->b_connected = false; 305 | break; 306 | case MODE_FILE: 307 | file.close_file(); 308 | break; 309 | default: 310 | break; 311 | } 312 | }; 313 | 314 | MerlinInterface::MerlinInterface() : socket(), file(), dtype(), 315 | head_buffer(), head(), tcp_buffer(), 316 | rcv(), ds_merlin(), 317 | b_raw(true), b_binary(true), 318 | nx(256), ny(256), 319 | data_depth(1), mode(MODE_FILE), 320 | acq(), acq_header(){}; -------------------------------------------------------------------------------- /src/cameras/MerlinInterface.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifndef MERLIN_INTERFACE_H 15 | #define MERLIN_INTERFACE_H 16 | 17 | #ifdef _MSC_VER 18 | #define _CRT_SECURE_NO_DEPRECATE 19 | #define _CRT_SECURE_NO_WARNINGS 20 | #endif 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "SocketConnector.h" 29 | #include "FileConnector.h" 30 | 31 | class MerlinInterface 32 | { 33 | private: 34 | enum Mode 35 | { 36 | MODE_FILE, 37 | MODE_TCP 38 | }; 39 | 40 | SocketConnector *socket; 41 | FileConnector file; 42 | 43 | std::string dtype; 44 | std::array head_buffer; 45 | std::array head; 46 | std::array tcp_buffer; 47 | std::string rcv; 48 | 49 | // Data Properties 50 | int ds_merlin; 51 | bool b_raw; 52 | bool b_binary; 53 | 54 | inline void read_head_data(); 55 | void init_uv(std::vector &u, std::vector &v); 56 | 57 | protected: 58 | int nx; 59 | int ny; 60 | int data_depth; 61 | Mode mode; 62 | 63 | std::string acq; 64 | std::vector acq_header; 65 | 66 | template 67 | inline void convert_binary_to_chars(std::vector &data); 68 | // Reading and decoding header data 69 | inline bool read_head(bool decode = true); 70 | inline void read_data(char *buffer, int data_size); 71 | inline int read_aquisition_header(); 72 | inline int decode_tcp_head(); 73 | inline void decode_head(); 74 | 75 | public: 76 | // Public Methods 77 | MerlinInterface(); 78 | int pre_run(std::vector &u, std::vector &v); 79 | template 80 | void read_frame(std::vector &data, bool dump_head); 81 | void init_interface(SocketConnector *socket); 82 | void init_interface(const std::string &path); 83 | void close_interface(); 84 | }; 85 | 86 | // Merlin Settings (to send to the Camera) 87 | struct MerlinSettings 88 | { 89 | int com_port; 90 | int hvbias; 91 | int threshold0; 92 | int threshold1; 93 | bool continuousrw; 94 | float dwell_time; // unit ms 95 | bool save; 96 | bool trigger; // false: internal, true: rising edge 97 | bool headless; 98 | bool raw; 99 | MerlinSettings() : com_port(6341), hvbias(120), threshold0(0), threshold1(511), continuousrw(true), dwell_time(0.1), save(false), trigger(true), headless(true), raw(true){}; 100 | }; 101 | #endif // MERLIN_INTERFACE_H -------------------------------------------------------------------------------- /src/cameras/MerlinWrapper.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifndef MERLIN_WRAPPER_H 15 | #define MERLIN_WRAPPER_H 16 | 17 | #include "Camera.h" 18 | #include "MerlinInterface.h" 19 | #include "Ricom.h" 20 | 21 | using namespace CAMERA; 22 | 23 | // Default constructor and definition of default configurations 24 | template <> 25 | Camera::Camera() 26 | { 27 | model = MERLIN; 28 | type = FRAME_BASED; 29 | data_depth = 1; 30 | nx = 256; 31 | ny = 256; 32 | swap_endian = true; 33 | }; 34 | template <> 35 | Camera::Camera(){}; 36 | 37 | // Constructor with camera_base as argument 38 | template <> 39 | Camera::Camera(Camera_BASE &cam) 40 | { 41 | type = FRAME_BASED; 42 | data_depth = cam.depth; 43 | nx = cam.nx_cam; 44 | ny = cam.ny_cam; 45 | swap_endian = true; 46 | u = cam.u; 47 | v = cam.v; 48 | }; 49 | template <> 50 | Camera::Camera(Camera_BASE &cam) { (void)cam; }; 51 | 52 | // Read frame method wrapper 53 | template <> 54 | template 55 | void Camera::read_frame(std::vector &data, bool b_first) 56 | { 57 | MerlinInterface::read_frame(data, b_first); 58 | }; 59 | // Template Specializations to avoid linker issues 60 | template void Camera::read_frame(std::vector &data, bool dump_head); 61 | template void Camera::read_frame(std::vector &data, bool dump_head); 62 | 63 | // Run method wrapper 64 | template <> 65 | void Camera::run(Ricom *ricom) 66 | { 67 | switch (ricom->mode) 68 | { 69 | case RICOM::modes::FILE: 70 | MerlinInterface::init_interface(ricom->file_path); 71 | break; 72 | case RICOM::modes::TCP: 73 | MerlinInterface::init_interface(&ricom->socket); 74 | break; 75 | } 76 | int bits = MerlinInterface::pre_run(ricom->camera.u, ricom->camera.v); 77 | switch (bits) 78 | { 79 | case 8: 80 | ricom->process_data(this); 81 | break; 82 | case 16: 83 | ricom->process_data(this); 84 | break; 85 | default: 86 | perror("Camera::run: pre_run returned an error!"); 87 | break; 88 | } 89 | close_interface(); 90 | }; 91 | template <> 92 | void Camera::run(Ricom *ricom) { (void)ricom; }; 93 | #endif // MERLIN_WRAPPER_H -------------------------------------------------------------------------------- /src/cameras/TimepixInterface.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #include "TimepixInterface.h" 15 | 16 | // Read a frame and compute COM 17 | void TimepixInterface::read_frame_com(std::atomic &idx, std::vector &dose_map, 18 | std::vector &sumx_map, std::vector &sumy_map, 19 | std::vector &stem_map, bool b_stem, 20 | std::array &offset, std::array &radius2, 21 | size_t first_frame, size_t end_frame) 22 | { 23 | e_event ev; 24 | 25 | while (true) 26 | { 27 | read_event(ev); 28 | size_t probe_position = floor(ev.toa * 25 / dt); 29 | if (probe_position >= first_frame && probe_position < end_frame) 30 | { 31 | size_t x = ev.index % nx; 32 | size_t y = floor(ev.index / nx); 33 | size_t probe_position2 = probe_position - first_frame; 34 | dose_map[probe_position2]++; 35 | sumx_map[probe_position2] += x; 36 | sumy_map[probe_position2] += y; 37 | if (b_stem) 38 | { 39 | float d2 = pow((float)x - offset[0], 2) + pow((float)y - offset[1], 2); 40 | if (d2 > radius2[0] && d2 <= radius2[1]) 41 | { 42 | stem_map[probe_position2]++; 43 | } 44 | } 45 | } 46 | if (probe_position > idx) 47 | { 48 | break; 49 | } 50 | } 51 | } 52 | 53 | // Read a frame and compute COM and create frame representation 54 | template 55 | void TimepixInterface::read_frame_com(std::atomic &idx, std::vector &dose_map, 56 | std::vector &sumx_map, std::vector &sumy_map, 57 | std::vector &stem_map, bool b_stem, 58 | std::array &offset, std::array &radius2, 59 | std::vector &frame, size_t frame_id, 60 | size_t first_frame, size_t end_frame) 61 | { 62 | e_event ev; 63 | 64 | while (true) 65 | { 66 | read_event(ev); 67 | size_t probe_position = floor(ev.toa * 25 / dt); 68 | if (probe_position >= first_frame && probe_position < end_frame) // && ev.toa * 25 % dt < dt/10 69 | { 70 | size_t x = ev.index % nx; 71 | size_t y = floor(ev.index / nx); 72 | size_t probe_position2 = probe_position - first_frame; 73 | dose_map[probe_position2]++; 74 | sumx_map[probe_position2] += x; 75 | sumy_map[probe_position2] += y; 76 | if (probe_position == frame_id) 77 | frame[x + y * nx]++; 78 | if (b_stem) 79 | { 80 | float d2 = pow((float)x - offset[0], 2) + pow((float)y - offset[1], 2); 81 | if (d2 > radius2[0] && d2 <= radius2[1]) 82 | { 83 | stem_map[probe_position2]++; 84 | } 85 | } 86 | } 87 | if (probe_position > idx) 88 | { 89 | break; 90 | } 91 | } 92 | } 93 | template void TimepixInterface::read_frame_com(std::atomic &idx, std::vector &dose_map, 94 | std::vector &sumx_map, std::vector &sumy_map, 95 | std::vector &stem_map, bool b_stem, 96 | std::array &offset, std::array &radius, 97 | std::vector &frame, size_t frame_id, 98 | size_t first_frame, size_t end_frame); 99 | template void TimepixInterface::read_frame_com(std::atomic &idx, std::vector &dose_map, 100 | std::vector &sumx_map, std::vector &sumy_map, 101 | std::vector &stem_map, bool b_stem, 102 | std::array &offset, std::array &radius, 103 | std::vector &frame, size_t frame_id, 104 | size_t first_frame, size_t end_frame); 105 | template void TimepixInterface::read_frame_com(std::atomic &idx, std::vector &dose_map, 106 | std::vector &sumx_map, std::vector &sumy_map, 107 | std::vector &stem_map, bool b_stem, 108 | std::array &offset, std::array &radius, 109 | std::vector &frame, size_t frame_id, 110 | size_t first_frame, size_t end_frame); 111 | 112 | void TimepixInterface::read_event(e_event &ev) 113 | { 114 | switch (mode) 115 | { 116 | case MODE_FILE: 117 | file.read_data((char *)&ev, sizeof(ev)); 118 | break; 119 | case MODE_TCP: // not implemented yet 120 | break; 121 | } 122 | } 123 | 124 | void TimepixInterface::init_interface(const std::string &t3p_path) 125 | { 126 | mode = MODE_FILE; 127 | file.path = t3p_path; 128 | file.open_file(); 129 | }; 130 | 131 | void TimepixInterface::close_interface() 132 | { 133 | file.close_file(); 134 | }; -------------------------------------------------------------------------------- /src/cameras/TimepixInterface.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifndef TIMEPIX_INTERFACE_H 15 | #define TIMEPIX_INTERFACE_H 16 | 17 | #ifdef __GNUC__ 18 | #define PACK(__Declaration__) __Declaration__ __attribute__((__packed__)) 19 | #endif 20 | 21 | #ifdef _MSC_VER 22 | #define PACK(__Declaration__) __pragma(pack(push, 1)) __Declaration__ __pragma(pack(pop)) 23 | #endif 24 | 25 | #define _USE_MATH_DEFINES 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "FileConnector.h" 32 | 33 | PACK(struct e_event 34 | { 35 | uint32_t index; 36 | uint64_t toa; 37 | uint8_t overflow; 38 | uint8_t ftoa; 39 | uint16_t tot; 40 | }); 41 | 42 | class TimepixInterface 43 | { 44 | private: 45 | FileConnector file; 46 | 47 | protected: 48 | enum Mode 49 | { 50 | MODE_FILE, 51 | MODE_TCP 52 | }; 53 | Mode mode; 54 | int nx; 55 | int ny; 56 | int dt; // unit: ns 57 | 58 | public: 59 | void read_frame_com(std::atomic &idx, std::vector &dose_map, 60 | std::vector &sumx_map, std::vector &sumy_map, 61 | std::vector &stem_map, bool b_stem, 62 | std::array &offset, std::array &radius, 63 | size_t first_frame, size_t end_frame 64 | ); 65 | 66 | template 67 | void read_frame_com(std::atomic &idx, std::vector &dose_map, 68 | std::vector &sumx_map, std::vector &sumy_map, 69 | std::vector &stem_map, bool b_stem, 70 | std::array &offset, std::array &radius, 71 | std::vector &frame, size_t frame_id, 72 | size_t first_frame, size_t end_frame); 73 | 74 | inline void read_event(e_event &ev); 75 | void init_interface(const std::string &t3p_path); 76 | void close_interface(); 77 | 78 | TimepixInterface() : mode(MODE_FILE), nx(256), ny(256), dt(1000){}; 79 | }; 80 | #endif // TIMEPIX_INTERFACE_H -------------------------------------------------------------------------------- /src/cameras/TimepixWrapper.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #ifndef TIMEPIX_WRAPPER_H 15 | #define TIMEPIX_WRAPPER_H 16 | 17 | #include "Camera.h" 18 | #include "TimepixInterface.h" 19 | #include "Ricom.h" 20 | 21 | using namespace CAMERA; 22 | 23 | // Default constructor and definition of default configurations 24 | template <> 25 | Camera::Camera() 26 | { 27 | model = TIMEPIX; 28 | type = EVENT_BASED; 29 | nx = 256; 30 | ny = 256; 31 | swap_endian = false; 32 | dwell_time = 1000; 33 | }; 34 | template <> 35 | Camera::Camera(){}; 36 | 37 | // Constructor with camera_base as argument 38 | template <> 39 | Camera::Camera(Camera_BASE &cam) 40 | { 41 | type = EVENT_BASED; 42 | dt = cam.dwell_time; 43 | nx = cam.nx_cam; 44 | ny = cam.ny_cam; 45 | swap_endian = false; 46 | u = cam.u; 47 | v = cam.v; 48 | }; 49 | template <> 50 | Camera::Camera(Camera_BASE &cam) { (void)cam; }; 51 | 52 | // read_frame_com method wrapper 53 | template <> 54 | void Camera::read_frame_com(std::atomic &idx, 55 | std::vector &dose_map, 56 | std::vector &sumx_map, std::vector &sumy_map, 57 | std::vector &stem_map, bool b_stem, 58 | std::array &offset, std::array &radius, 59 | size_t first_frame, size_t end_frame) 60 | { 61 | TimepixInterface::read_frame_com(idx, dose_map, sumx_map, sumy_map, stem_map, b_stem, offset, radius, first_frame, end_frame); 62 | } 63 | 64 | // read_frame_com method wrapper 65 | template <> 66 | void Camera::read_frame_com_cbed(std::atomic &idx, 67 | std::vector &dose_map, 68 | std::vector &sumx_map, std::vector &sumy_map, 69 | std::vector &stem_map, bool b_stem, 70 | std::array &offset, std::array &radius, 71 | std::vector &frame, size_t frame_id, 72 | size_t first_frame, size_t end_frame) 73 | { 74 | TimepixInterface::read_frame_com(idx, dose_map, sumx_map, sumy_map, stem_map, b_stem, offset, radius, frame, frame_id, first_frame, end_frame); 75 | } 76 | 77 | // Run method wrapper 78 | template <> 79 | void Camera::run(Ricom *ricom) 80 | { 81 | switch (ricom->mode) 82 | { 83 | case RICOM::FILE: 84 | TimepixInterface::init_interface(ricom->file_path); 85 | ricom->process_data(this); 86 | close_interface(); 87 | break; 88 | case RICOM::TCP: 89 | // TimepixInterface::init_interface(ricom->socket); 90 | perror("TCP mode not supported for Timepix yet"); 91 | } 92 | }; 93 | template <> 94 | void Camera::run(Ricom *ricom) { (void)ricom; }; 95 | 96 | #endif // TIMEPIX_WRAPPER_H -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu, 2 | * University of Antwerp - All Rights Reserved. 3 | * You may use, distribute and modify 4 | * this code under the terms of the GPL3 license. 5 | * You should have received a copy of the GPL3 license with 6 | * this file. If not, please visit: 7 | * https://www.gnu.org/licenses/gpl-3.0.en.html 8 | * 9 | * Authors: 10 | * Thomas Friedrich 11 | * Chu-Ping Yu 12 | */ 13 | 14 | #include 15 | #include "Ricom.h" 16 | #include "Camera.h" 17 | 18 | static std::string version("0.0.4-beta"); 19 | 20 | // Forward Declarations 21 | int run_cli(int argc, char *argv[], Ricom *ricom, CAMERA::Default_configurations &hardware_configurations); 22 | int run_gui(Ricom *ricom, CAMERA::Default_configurations &hardware_configurations); 23 | void log2file(Ricom *ricom); 24 | 25 | # ifdef __linux__ 26 | #include 27 | std::string get_os() 28 | { 29 | struct utsname uts; 30 | uname(&uts); 31 | return std::string(uts.sysname) + " Kernel : " + std::string(uts.version); 32 | } 33 | #elif defined(_WIN32) || defined(WIN32) 34 | #include 35 | #include 36 | #include 37 | 38 | typedef LONG NTSTATUS, *PNTSTATUS; 39 | #define STATUS_SUCCESS (0x00000000) 40 | 41 | typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); 42 | 43 | std::string get_os() { 44 | HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); 45 | if (hMod) { 46 | RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); 47 | if (fxPtr != nullptr) { 48 | RTL_OSVERSIONINFOW rovi = { 0 }; 49 | rovi.dwOSVersionInfoSize = sizeof(rovi); 50 | if ( STATUS_SUCCESS == fxPtr(&rovi) ) { 51 | return "Windows " + std::to_string(rovi.dwMajorVersion) + "." + std::to_string(rovi.dwMinorVersion) ; 52 | } 53 | } 54 | } 55 | RTL_OSVERSIONINFOW rovi = { 0 }; 56 | return "Windows"; 57 | } 58 | #else 59 | std::string get_os() {return "Unknown"}; 60 | #endif 61 | 62 | int main(int argc, char *argv[]) 63 | { 64 | 65 | Ricom ricom; 66 | CAMERA::Default_configurations hardware_configurations; 67 | 68 | if (argc == 1) 69 | { 70 | #ifdef _WIN32 71 | FreeConsole(); 72 | #endif 73 | log2file(&ricom); 74 | return run_gui(&ricom, hardware_configurations); 75 | } 76 | else 77 | { 78 | return run_cli(argc, argv, &ricom, hardware_configurations); 79 | } 80 | } 81 | 82 | // Redirect all output of cout into a log-file for GUI mode 83 | void log2file(Ricom *ricom) 84 | { 85 | if (freopen("ricom.log", "a", stdout) == NULL) 86 | { 87 | std::cout << "Error redirecting output to log file" << std::endl; 88 | } 89 | if (freopen("ricom.log", "a", stderr) == NULL) 90 | { 91 | std::cout << "Error redirecting error output to log file" << std::endl; 92 | } 93 | ricom->b_print2file = true; 94 | auto t = std::time(nullptr); 95 | auto tm = *std::localtime(&t); 96 | std::cout << std::endl; 97 | std::cout << "Ricom version " << version << " started at " << std::put_time(&tm, "%d/%m/%Y %H:%M:%S") << std::endl; 98 | std::cout << "OS: " << get_os() << std::endl; 99 | } --------------------------------------------------------------------------------