├── .gitignore
├── .gitmodules
├── CMakeLists.txt
├── Dockerfile
├── LICENSE
├── README.md
├── assets
├── config.toml
├── demo.png
└── imgs
│ ├── doals_train_128.gif
│ ├── dufomap_leica.gif
│ └── two_floor_mid360.gif
├── main.py
└── src
├── dufomap.cpp
├── indicators.hpp
└── toml.hpp
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Compiled Object files
5 | *.slo
6 | *.lo
7 | *.o
8 | *.obj
9 |
10 | # Precompiled Headers
11 | *.gch
12 | *.pch
13 |
14 | # Compiled Dynamic libraries
15 | *.so
16 | *.dylib
17 | *.dll
18 |
19 | # Fortran module files
20 | *.mod
21 | *.smod
22 |
23 | # Compiled Static libraries
24 | *.lai
25 | *.la
26 | *.a
27 | *.lib
28 |
29 | # Executables
30 | *.exe
31 | *.out
32 | *.app
33 |
34 | # VS Code
35 | .vscode/*
36 |
37 | # CLion
38 | .idea/*
39 |
40 | # Tests
41 | ufomap_tests/*
42 |
43 | # Build folder
44 | build/*
45 |
46 | # Install folder
47 | install/*
48 |
49 | # Cache folder
50 | .cache/*
51 |
52 | # Data folder
53 | data/*
54 |
55 | # Docs
56 | docs/html/*
57 |
58 | # coverage report
59 | coverage_report/*
60 | .coverage/
61 |
62 | *.pcd
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "ufomap"]
2 | path = ufomap
3 | url = https://github.com/UnknownFreeOccupied/ufomap
4 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.17)
2 |
3 | project(dufomap
4 | VERSION 1.0.0
5 | DESCRIPTION "DUFOMap: Efficient Dynamic Awareness Mapping"
6 | LANGUAGES CXX
7 | )
8 | set(CMAKE_CXX_STANDARD 20)
9 | set(CMAKE_CXX_STANDARD_REQUIRED True)
10 | if(NOT CMAKE_BUILD_TYPE)
11 | set(CMAKE_BUILD_TYPE Release)
12 | endif()
13 |
14 | add_subdirectory(ufomap)
15 | include_directories(${CMAKE_SOURCE_DIR}/ufomap)
16 |
17 | add_executable(dufomap_run src/dufomap.cpp)
18 | set_target_properties(dufomap_run
19 | PROPERTIES
20 | VERSION ${PROJECT_VERSION}
21 | SOVERSION ${PROJECT_VERSION_MAJOR}
22 | CXX_STANDARD 20
23 | CXX_STANDARD_REQUIRED YES
24 | CXX_EXTENSIONS NO
25 | )
26 | target_link_libraries(dufomap_run PRIVATE UFO::Map)
27 | target_compile_features(dufomap_run
28 | PUBLIC
29 | cxx_std_20
30 | )
31 | target_compile_options(dufomap_run
32 | PRIVATE
33 | # -Wall
34 | # -Werror
35 | # -Wextra
36 | # -Wpedantic
37 | # -Wunreachable-code
38 | # -Wconversion
39 | # -Wcast-align
40 | # -Wunused
41 | # -Wno-unused-parameter
42 | # -Wold-style-cast
43 | # -Wpointer-arith
44 | # -Wcast-qual
45 | # -Wno-missing-braces
46 | # -Wdouble-promotion
47 |
48 | # -Wshadow
49 | # -march=native
50 | )
51 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:focal
2 | LABEL maintainer="Qingwen Zhang "
3 |
4 | # Just in case we need it
5 | ENV DEBIAN_FRONTEND noninteractive
6 |
7 | # install g++10 and tbb
8 | RUN apt update && apt install -y wget git zsh tmux vim g++ curl unzip
9 | RUN apt update && apt install -y gcc-10 g++-10 libtbb-dev liblz4-dev liblzf-dev pkg-config
10 |
11 | # install CMAKE
12 | RUN apt update && apt install -y gnupg gnupg2 software-properties-common
13 | RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null
14 | RUN apt-add-repository 'deb https://apt.kitware.com/ubuntu/ focal main' && apt update && apt install -y cmake
15 |
16 | # since we will output pcd file, don't want to root to lock it. normally 1000 is the first user in our desktop also
17 | RUN useradd -ms /bin/bash -u 1000 kin
18 | USER kin
19 |
20 | # setup oh-my-zsh
21 | RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
22 | RUN printf "y\ny\ny\n\n" | bash -c "$(curl -fsSL https://raw.githubusercontent.com/Kin-Zhang/Kin-Zhang/main/scripts/setup_ohmyzsh.sh)"
23 |
24 | RUN mkdir -p /home/kin/workspace && mkdir -p /home/kin/data
25 | RUN git clone --recurse-submodules -b main --single-branch https://github.com/KTH-RPL/dufomap /home/kin/workspace/dufomap
26 | WORKDIR /home/kin/workspace/dufomap
27 |
28 | # Run container: docker run -it --rm --name dufomap -v /home/kin/data:/home/kin/data zhangkin/dufomap /bin/zsh
29 | # you can also run with root user in existing container: docker exec -it -u 0 dufomap /bin/zsh
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2024, Daniel Duberg
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | 1. Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 |
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | 3. Neither the name of the copyright holder nor the names of its
16 | contributors may be used to endorse or promote products derived from
17 | this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
DUFOMap: Efficient Dynamic Awareness Mapping
3 |
4 |
5 | [](https://arxiv.org/abs/2403.01449)
6 | [](https://KTH-RPL.github.io/dufomap) [](https://mit-spark.github.io/Longterm-Perception-WS/assets/proceedings/DUFOMap/poster.pdf) [video coming soon]
7 |
8 | Quick Demo: Run with the **same parameter setting** without tuning for different sensor (e.g 16, 32, 64, and 128 channel LiDAR and Livox-series mid360), the following shows the data collected from:
9 |
10 | | Leica-RTC360 | 128-channel LiDAR | Livox-mid360 |
11 | | ------- | ------- | ------- |
12 | |  |  |  |
13 |
14 |
15 | 🚀 2024-11-20: Update dufomap Python API from [SeFlow](https://github.com/KTH-RPL/SeFlow) try it now! `pip install dufomap` and run `python main.py --data_dir data/00` to get the cleaned map directly. Support all >=Python 3.8 in Windows and Linux. Please extract your own data to unified format first follow [this wiki page](https://kth-rpl.github.io/DynamicMap_Benchmark/data/creation/#custom-data).
16 |
17 | ## 0. Setup
18 |
19 | ```bash
20 | sudo apt update && sudo apt install gcc-10 g++-10
21 | sudo apt install libtbb-dev liblz4-dev
22 | ```
23 |
24 |
25 | Clone quickly and init submodules:
26 | ```bash
27 | git clone --recursive -b main --single-branch https://github.com/KTH-RPL/dufomap.git
28 | ```
29 |
30 | Or you can directly build docker image through our [Dockerfile](Dockerfile):
31 | ```bash
32 | docker build -t dufomap .
33 | ```
34 |
35 | ## 1. Build & Run
36 |
37 | Build:
38 |
39 | ```bash
40 | cmake -B build -D CMAKE_CXX_COMPILER=g++-10 && cmake --build build
41 | ```
42 |
43 | Prepare Data: Teaser data (KITTI 00: 384.4Mb) can be downloaded via follow commands, more data detail can be found in the [dataset section](https://kth-rpl.github.io/DynamicMap_Benchmark/data) or format your own dataset follow [custom dataset section](https://kth-rpl.github.io/DynamicMap_Benchmark/data/creation/#custom-data).
44 |
45 | ```bash
46 | wget https://zenodo.org/records/8160051/files/00.zip -p data
47 | unzip data/00.zip -d data
48 | ```
49 |
50 | Run:
51 |
52 | ```bash
53 | ./build/dufomap_run data/00 assets/config.toml
54 | ```
55 |
56 | 
57 |
58 | ## 2. Evaluation
59 |
60 | Please reference to [DynamicMap_Benchmark](https://github.com/KTH-RPL/DynamicMap_Benchmark) for the evaluation of DUFOMap and comparison with other dynamic removal methods.
61 |
62 | [Evaluation Section link](https://github.com/KTH-RPL/DynamicMap_Benchmark/blob/master/scripts/README.md#evaluation)
63 |
64 |
65 | ## Acknowledgements
66 |
67 | Thanks to HKUST Ramlab's members: Bowen Yang, Lu Gan, Mingkai Tang, and Yingbing Chen, who help collect additional datasets.
68 |
69 | This work was partially supported by the Wallenberg AI, Autonomous Systems and Software Program ([WASP](https://wasp-sweden.org/)) funded by the Knut and Alice Wallenberg Foundation including the WASP NEST PerCorSo.
70 |
71 | Feel free to explore below projects that use [ufomap](https://github.com/UnknownFreeOccupied/ufomap) (attach code links as follows):
72 | - [RA-L'24 DUFOMap, Dynamic Awareness]()
73 | - [RA-L'23 SLICT, SLAM](https://github.com/brytsknguyen/slict)
74 | - [RA-L'20 UFOMap, Mapping Framework](https://github.com/UnknownFreeOccupied/ufomap)
75 |
76 | ### Citation
77 |
78 | Please cite our works if you find these useful for your research.
79 |
80 | ```
81 | @article{daniel2024dufomap,
82 | author={Duberg, Daniel and Zhang, Qingwen and Jia, MingKai and Jensfelt, Patric},
83 | journal={IEEE Robotics and Automation Letters},
84 | title={{DUFOMap}: Efficient Dynamic Awareness Mapping},
85 | year={2024},
86 | volume={9},
87 | number={6},
88 | pages={1-8},
89 | doi={10.1109/LRA.2024.3387658}
90 | }
91 | @article{duberg2020ufomap,
92 | author={Duberg, Daniel and Jensfelt, Patric},
93 | journal={IEEE Robotics and Automation Letters},
94 | title={{UFOMap}: An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown},
95 | year={2020},
96 | volume={5},
97 | number={4},
98 | pages={6411-6418},
99 | doi={10.1109/LRA.2020.3013861}
100 | }
101 | @inproceedings{zhang2023benchmark,
102 | author={Zhang, Qingwen and Duberg, Daniel and Geng, Ruoyu and Jia, Mingkai and Wang, Lujia and Jensfelt, Patric},
103 | booktitle={IEEE 26th International Conference on Intelligent Transportation Systems (ITSC)},
104 | title={A Dynamic Points Removal Benchmark in Point Cloud Maps},
105 | year={2023},
106 | pages={608-614},
107 | doi={10.1109/ITSC57777.2023.10422094}
108 | }
109 | ```
110 |
--------------------------------------------------------------------------------
/assets/config.toml:
--------------------------------------------------------------------------------
1 |
2 | [important]
3 | resolution = 0.1 # voxel size (meters), v in the paper, default 0.1
4 | inflate_unknown = 1 # d_p in the paper, default 1
5 | inflate_hits_dist = 0.2 # d_s in the paper, default 0.2
6 |
7 |
8 | # ----------- No need to modify if you are not professional dufoer or ufoer.
9 | # ----------- Deeper Parameter for debugging potential better result --------
10 |
11 | [integration]
12 | inflate_unknown_compensation = true
13 | ray_passthrough_hits = false
14 | down_sampling_method = "center" # ["none", "center", "centroid", "uniform"]
15 | max_range = -1 # Maximum range to integrate
16 | only_valid = false # Only do ray casting for points within 'max_range'
17 | hit_depth = 0 # Level of the octree to integrate hits
18 | miss_depth = 0 # Level of the octree to integrate misses
19 | ray_casting_depth = 0 # Level of the octree to perform ray casting
20 | simple_ray_casting = false
21 | simple_ray_casting_factor = 1.0
22 | parallel = true # Use parallel execution for integration
23 | num_threads = 0 # Number of threads to use, 8 times number of physical cores seems to be a good number and it is chosen if 0 is given
24 |
25 | [map]
26 | levels = 20 # Levels of the octree
27 |
28 | [dataset]
29 | first = 0 # First pcd index
30 | last = -1 # Last pcd index (-1 equals all)
31 | num = -1 # Number of pcd files to read (-1 equals all)
32 |
33 | [printing]
34 | verbose = false
35 | debug = false
36 |
37 | [output]
38 | filename = "dufomap_output"
39 | raycasting = false
40 |
--------------------------------------------------------------------------------
/assets/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KTH-RPL/dufomap/5b2884f6c8efa3f035dd538a68e72296dc205ca2/assets/demo.png
--------------------------------------------------------------------------------
/assets/imgs/doals_train_128.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KTH-RPL/dufomap/5b2884f6c8efa3f035dd538a68e72296dc205ca2/assets/imgs/doals_train_128.gif
--------------------------------------------------------------------------------
/assets/imgs/dufomap_leica.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KTH-RPL/dufomap/5b2884f6c8efa3f035dd538a68e72296dc205ca2/assets/imgs/dufomap_leica.gif
--------------------------------------------------------------------------------
/assets/imgs/two_floor_mid360.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KTH-RPL/dufomap/5b2884f6c8efa3f035dd538a68e72296dc205ca2/assets/imgs/two_floor_mid360.gif
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | """
2 | # Created: 2024-11-20 13:11
3 | # Copyright (C) 2024-now, RPL, KTH Royal Institute of Technology
4 | # Author: Qingwen Zhang (https://kin-zhang.github.io/)
5 | #
6 | # This file is part of DUFOMap (https://github.com/KTH-RPL/dufomap) and
7 | # DynamicMap Benchmark (https://github.com/KTH-RPL/DynamicMap_Benchmark) projects.
8 | # If you find this repo helpful, please cite the respective publication as
9 | # listed on the above website.
10 |
11 | # Description: Output Cleaned Map through Python API.
12 | """
13 | from pathlib import Path
14 | import os, fire, time
15 | import numpy as np
16 | from tqdm import tqdm
17 |
18 | from dufomap import dufomap
19 | from dufomap.utils import pcdpy3
20 | def inv_pose_matrix(pose):
21 | inv_pose = np.eye(4)
22 | inv_pose[:3, :3] = pose[:3, :3].T
23 | inv_pose[:3, 3] = -pose[:3, :3].T.dot(pose[:3, 3])
24 | return inv_pose
25 | class DynamicMapData:
26 | def __init__(self, directory):
27 | self.scene_id = directory.split("/")[-1]
28 | self.directory = Path(directory) / "pcd"
29 | self.pcd_files = [os.path.join(self.directory, f) for f in sorted(os.listdir(self.directory)) if f.endswith('.pcd')]
30 |
31 | def __len__(self):
32 | return len(self.pcd_files)
33 |
34 | def __getitem__(self, index_):
35 | res_dict = {
36 | 'scene_id': self.scene_id,
37 | 'timestamp': self.pcd_files[index_].split("/")[-1].split(".")[0],
38 | }
39 | pcd_ = pcdpy3.PointCloud.from_path(self.pcd_files[index_])
40 | pc0 = pcd_.np_data[:,:3]
41 | res_dict['pc'] = pc0.astype(np.float32)
42 | res_dict['pose'] = list(pcd_.viewpoint)
43 | return res_dict
44 |
45 | def main_vis(
46 | data_dir: str = "/home/kin/data/00",
47 | ):
48 | dataset = DynamicMapData(data_dir)
49 |
50 | # STEP 0: initialize
51 | mydufo = dufomap(0.1, 0.2, 2, num_threads=12) # resolution, d_s, d_p same with paper.
52 | cloud_acc = np.zeros((0, 3), dtype=np.float32)
53 | for data_id in (pbar := tqdm(range(0, len(dataset)),ncols=100)):
54 | data = dataset[data_id]
55 | now_scene_id = data['scene_id']
56 | pbar.set_description(f"id: {data_id}, scene_id: {now_scene_id}, timestamp: {data['timestamp']}")
57 |
58 | # STEP 1: integrate point cloud into dufomap
59 | mydufo.run(data['pc'], data['pose'], cloud_transform = False) # since pc already in world frame
60 | cloud_acc = np.concatenate((cloud_acc, data['pc']), axis=0)
61 |
62 | # STEP 2: propagate
63 | mydufo.oncePropagateCluster(if_propagate=True, if_cluster=False)
64 | # STEP 3: Map results
65 | mydufo.outputMap(cloud_acc, voxel_map=False)
66 | # NOTE(Qingwen): You can also save voxeled map directly based on the resolution we set before:
67 | # mydufo.outputMap(cloud_acc, voxel_map=True)
68 |
69 | mydufo.printDetailTiming()
70 |
71 | if __name__ == "__main__":
72 | start_time = time.time()
73 | fire.Fire(main_vis)
74 | print(f"Time used: {time.time() - start_time:.2f} s")
--------------------------------------------------------------------------------
/src/dufomap.cpp:
--------------------------------------------------------------------------------
1 | // UFO
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | // TOML
16 | #include "toml.hpp"
17 | #include "indicators.hpp"
18 |
19 | // STL
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | #ifdef UFO_PARALLEL
34 | // STL
35 | #include
36 | #endif
37 |
38 | struct Dataset {
39 | std::size_t first = 0;
40 | std::size_t last = -1;
41 | std::size_t num = -1;
42 | };
43 |
44 | struct Map {
45 | ufo::node_size_t resolution = 0.1; // In meters
46 | ufo::depth_t levels = 17; // Levels of the octree
47 | };
48 |
49 | struct Clustering {
50 | bool cluster = false;
51 | float max_distance = 0.2f;
52 | std::size_t min_points = 0;
53 | ufo::depth_t depth = 0;
54 | };
55 |
56 | struct Printing {
57 | bool verbose = true;
58 | bool debug = false;
59 | };
60 |
61 | struct Output {
62 | std::string filename = "output";
63 | bool has_color = false;
64 | bool raycasting = false;
65 | };
66 |
67 | struct Config {
68 | Dataset dataset;
69 | Map map;
70 | ufo::IntegrationParams integration;
71 | bool propagate = false;
72 | Clustering clustering;
73 | Printing printing;
74 | Output output;
75 |
76 | void read(toml::table tbl)
77 | {
78 | map.resolution = read(tbl["important"]["resolution"], map.resolution);
79 |
80 | dataset.first = read(tbl["dataset"]["first"], dataset.first);
81 | dataset.last = read(tbl["dataset"]["last"], dataset.last);
82 | dataset.num = read(tbl["dataset"]["num"], dataset.num);
83 | map.levels = read(tbl["map"]["levels"], map.levels);
84 |
85 | auto dsm = read(tbl["integration"]["down_sampling_method"], std::string("none"));
86 | integration.down_sampling_method =
87 | "none" == dsm
88 | ? ufo::DownSamplingMethod::NONE
89 | : ("centroid" == dsm ? ufo::DownSamplingMethod::CENTROID
90 | : ("uniform" == dsm ? ufo::DownSamplingMethod::UNIFORM
91 | : ufo::DownSamplingMethod::CENTER));
92 | integration.hit_depth = read(tbl["integration"]["hit_depth"], integration.hit_depth);
93 | integration.miss_depth =
94 | read(tbl["integration"]["miss_depth"], integration.miss_depth);
95 | integration.min_range = read(tbl["integration"]["min_range"], integration.min_range);
96 | integration.max_range = read(tbl["integration"]["max_range"], integration.max_range);
97 | integration.inflate_unknown =
98 | read(tbl["important"]["inflate_unknown"], integration.inflate_unknown);
99 | integration.inflate_unknown_compensation =
100 | read(tbl["integration"]["inflate_unknown_compensation"],
101 | integration.inflate_unknown_compensation);
102 | integration.ray_passthrough_hits = read(tbl["integration"]["ray_passthrough_hits"],
103 | integration.ray_passthrough_hits);
104 | integration.inflate_hits_dist =
105 | read(tbl["important"]["inflate_hits_dist"], integration.inflate_hits_dist);
106 | integration.ray_casting_method = read(tbl["integration"]["simple_ray_casting"], true)
107 | ? ufo::RayCastingMethod::SIMPLE
108 | : ufo::RayCastingMethod::PROPER;
109 | integration.simple_ray_casting_factor =
110 | read(tbl["integration"]["simple_ray_casting_factor"],
111 | integration.simple_ray_casting_factor);
112 | integration.parallel = tbl["integration"]["parallel"].value_or(integration.parallel);
113 | integration.num_threads =
114 | read(tbl["integration"]["num_threads"], integration.num_threads);
115 | integration.only_valid =
116 | read(tbl["integration"]["only_valid"], integration.only_valid);
117 |
118 | propagate = read(tbl["integration"]["propagate"], propagate);
119 |
120 | clustering.cluster = read(tbl["clustering"]["cluster"], clustering.cluster);
121 | clustering.max_distance =
122 | read(tbl["clustering"]["max_distance"], clustering.max_distance);
123 | clustering.min_points = read(tbl["clustering"]["min_points"], clustering.min_points);
124 | clustering.depth = read(tbl["clustering"]["depth"], clustering.depth);
125 |
126 | printing.verbose = read(tbl["printing"]["verbose"], printing.verbose);
127 | printing.debug = read(tbl["printing"]["debug"], printing.debug);
128 |
129 | output.filename = read(tbl["output"]["filename"], output.filename);
130 | output.has_color = read(tbl["output"]["has_color"], output.has_color);
131 | output.raycasting = read(tbl["output"]["raycasting"], output.raycasting);
132 | }
133 |
134 | void save() const
135 | {
136 | // TODO: Implement
137 | }
138 |
139 | private:
140 | template
141 | std::remove_cvref_t read(toml::node_view node, T&& default_value)
142 | {
143 | // if (!node.is_value()) {
144 | // node.as_array()->push_back("MISSING");
145 | // missing_config = true;
146 | // std::cout << node << '\n';
147 | // return default_value;
148 | // }
149 |
150 | return node.value_or(default_value);
151 | }
152 |
153 | private:
154 | bool missing_config{false};
155 | bool wrong_config{false};
156 | };
157 |
158 | std::ostream& operator<<(std::ostream& out, Config const& config)
159 | {
160 | out << "Config\n";
161 |
162 | out << "\tDataset\n";
163 | out << "\t\tFirst: " << config.dataset.first << '\n';
164 | out << "\t\tLast: ";
165 | if (-1 == config.dataset.last) {
166 | out << -1 << '\n';
167 | } else {
168 | out << config.dataset.last << '\n';
169 | }
170 | out << "\t\tNum: ";
171 | if (-1 == config.dataset.num) {
172 | out << -1 << '\n';
173 | } else {
174 | out << config.dataset.num << '\n';
175 | }
176 |
177 | out << "\tMap\n";
178 | out << "\t\tResolution: " << config.map.resolution << '\n';
179 | out << "\t\tLevels: " << +config.map.levels << '\n';
180 |
181 | out << "\tIntegration\n";
182 | out << "\t\tDown sampling method: "
183 | << (ufo::DownSamplingMethod::NONE == config.integration.down_sampling_method
184 | ? "none"
185 | : (ufo::DownSamplingMethod::CENTER ==
186 | config.integration.down_sampling_method
187 | ? "center"
188 | : (ufo::DownSamplingMethod::CENTROID ==
189 | config.integration.down_sampling_method
190 | ? "centroid"
191 | : "uniform")))
192 | << '\n';
193 | out << "\t\tHit depth: " << +config.integration.hit_depth << '\n';
194 | out << "\t\tMiss depth: " << +config.integration.miss_depth << '\n';
195 | out << "\t\tMin range: " << config.integration.min_range << '\n';
196 | out << "\t\tMax range: " << config.integration.max_range << '\n';
197 | out << "\t\tInflate unknown " << config.integration.inflate_unknown
198 | << '\n';
199 | out << "\t\tInflate unknown compensation "
200 | << config.integration.inflate_unknown_compensation << '\n';
201 | out << "\t\tRay passthrough hits " << config.integration.ray_passthrough_hits
202 | << '\n';
203 | out << "\t\tInflate hits dist " << config.integration.inflate_hits_dist
204 | << '\n';
205 | out << "\t\tEarly stop distance: " << config.integration.early_stop_distance
206 | << '\n';
207 | out << "\t\tSimple ray casting: " << std::boolalpha
208 | << (ufo::RayCastingMethod::SIMPLE == config.integration.ray_casting_method ? true
209 | : false)
210 | << '\n';
211 | out << "\t\tSimple ray casting factor: "
212 | << config.integration.simple_ray_casting_factor << '\n';
213 | out << "\t\tParallel: " << config.integration.parallel << '\n';
214 | out << "\t\tNum threads: " << config.integration.num_threads << '\n';
215 | out << "\t\tOnly valid: " << config.integration.only_valid << '\n';
216 | out << "\t\tSliding window size: " << config.integration.sliding_window_size
217 | << '\n';
218 | out << "\t\tPropagate: " << std::boolalpha << config.propagate
219 | << '\n';
220 |
221 | out << "\tClustering\n";
222 | out << "\t\tCluster: " << std::boolalpha << config.clustering.cluster << '\n';
223 | out << "\t\tMax distance: " << config.clustering.max_distance << '\n';
224 | out << "\t\tMin points: " << config.clustering.min_points << '\n';
225 | out << "\t\tDepth: " << +config.clustering.depth << '\n';
226 |
227 | out << "\tPrinting\n";
228 | out << "\t\tVerbose: " << std::boolalpha << config.printing.verbose << '\n';
229 | out << "\t\tDebug: " << std::boolalpha << config.printing.debug << '\n';
230 |
231 | out << "\tOutput\n";
232 | out << "\t\tFilename: " << config.output.filename << '\n';
233 | out << "\t\tHas color: " << std::boolalpha << config.output.has_color << '\n';
234 | out << "\t\tRaycasting: " << config.output.raycasting << '\n';
235 |
236 | return out;
237 | }
238 |
239 | Config readConfig(std::filesystem::path path)
240 | {
241 | Config config;
242 | for (;;) {
243 | if (std::filesystem::exists(path)) {
244 | toml::table tbl;
245 | try {
246 | tbl = toml::parse_file((path).string());
247 | } catch (toml::parse_error const& err) {
248 | std::cerr << "Configuration parsing failed:\n" << err << '\n';
249 | exit(1);
250 | }
251 |
252 | config.read(tbl);
253 | if (config.printing.verbose) {
254 | std::cout << "Found: " << (path) << '\n';
255 | }
256 |
257 | break;
258 | }
259 | if (!path.has_parent_path()) {
260 | std::cout << "Did not find configuration file, using default.\n";
261 | break;
262 | }
263 | path = path.parent_path();
264 | }
265 |
266 | if (config.printing.verbose) {
267 | std::cout << config << '\n';
268 | }
269 |
270 | return config;
271 | }
272 |
273 | ufo::Color randomColor()
274 | {
275 | static std::random_device rd;
276 | static std::mt19937 gen(rd());
277 | static std::uniform_int_distribution dis(0, -1);
278 | return {dis(gen), dis(gen), dis(gen)};
279 | }
280 |
281 | template
282 | void cluster(Map& map, Clustering const& clustering)
283 | {
284 | std::unordered_set seen;
285 | std::vector queue;
286 |
287 | auto depth = clustering.depth;
288 | auto max_distance = clustering.max_distance;
289 | auto min_points = clustering.min_points;
290 |
291 | ufo::label_t l{1};
292 | for (auto node : map.query(ufo::pred::Leaf(depth) && ufo::pred::SeenFree() &&
293 | ufo::pred::HitsMin(1) && ufo::pred::Label(0))) {
294 | if (map.label(node.index())) { // FIXME: This is because how the iterator works
295 | continue;
296 | }
297 |
298 | seen = {node};
299 | queue.assign(1, ufo::Sphere(map.center(node), max_distance));
300 |
301 | map.setLabel(node, l, false);
302 |
303 | while (!queue.empty()) {
304 | auto p = ufo::pred::Intersects(queue);
305 | queue.clear();
306 | for (auto const& node : map.query(
307 | ufo::pred::Leaf(depth) && ufo::pred::SeenFree() && ufo::pred::HitsMin(1) &&
308 | ufo::pred::Label(0) && std::move(p) &&
309 | ufo::pred::Satisfies([&seen](auto n) { return seen.insert(n).second; }))) {
310 | queue.emplace_back(map.center(node), max_distance);
311 | map.setLabel(node, l, false);
312 | }
313 | }
314 |
315 | if (seen.size() < min_points) {
316 | for (auto e : seen) {
317 | if (l == map.label(e)) {
318 | map.setLabel(e, -1, false);
319 | }
320 | }
321 | }
322 |
323 | l += seen.size() >= min_points;
324 |
325 | map.propagateModified(); // FIXME: Should this be here?
326 | }
327 | }
328 |
329 | int main(int argc, char* argv[])
330 | {
331 | if (1 >= argc) {
332 | std::cout << "[ERROR] Please running by: " << argv[0] << " [pcd_folder] [optional: config_file_path]";
333 | return 0;
334 | }
335 |
336 | std::filesystem::path path(argv[1]);
337 | std::string config_file_path;
338 | if (argc > 2)
339 | config_file_path = argv[2];
340 | else
341 | config_file_path = std::string(argv[1]) + "/dufomap.toml";
342 |
343 | auto config = readConfig(std::filesystem::path(config_file_path));
344 | std::cout << "[LOG] Step 1: Successfully read configuration from: " << config_file_path < map(
346 | config.map.resolution, config.map.levels);
347 | map.reserve(100'000'000);
348 |
349 | std::vector pcds;
350 | for (const auto& entry : std::filesystem::directory_iterator(path / "pcd")) {
351 | if (!entry.is_regular_file()) {
352 | continue;
353 | }
354 | std::size_t i = std::stoul(entry.path().stem());
355 | if (config.dataset.first <= i && config.dataset.last >= i) {
356 | pcds.push_back(entry.path().filename());
357 | }
358 | }
359 | std::ranges::sort(pcds);
360 | // std::cout << config << std::endl;
361 | pcds.resize(std::min(pcds.size(), config.dataset.num));
362 |
363 | ufo::Timing timing;
364 | timing.start("Total");
365 |
366 | ufo::PointCloudColor cloud_acc;
367 |
368 | std::cout << "[LOG] Step 2: Starting Processing data from: " << path << '\n';
369 | indicators::show_console_cursor(false);
370 | indicators::BlockProgressBar bar{
371 | indicators::option::BarWidth{50},
372 | indicators::option::Start{"["},
373 | indicators::option::End{"]"},
374 | indicators::option::PrefixText{"[LOG] Running dufomap "},
375 | indicators::option::ForegroundColor{indicators::Color::white},
376 | indicators::option::ShowElapsedTime{true},
377 | indicators::option::ShowRemainingTime{true},
378 | indicators::option::FontStyles{std::vector{indicators::FontStyle::bold}}
379 | };
380 |
381 | for (std::size_t i{}; std::string filename : pcds) {
382 | bar.set_progress(100 * i / pcds.size());
383 | ++i;
384 | timing.setTag("Total " + std::to_string(i) + " of " + std::to_string(pcds.size()) +
385 | " (" + std::to_string(100 * i / pcds.size()) + "%)");
386 |
387 | ufo::PointCloudColor cloud;
388 | ufo::Pose6f viewpoint;
389 | timing[1].start("Read");
390 | ufo::readPointCloudPCD(path / "pcd" / filename, cloud, viewpoint);
391 | timing[1].stop();
392 |
393 | cloud_acc.insert(std::end(cloud_acc), std::cbegin(cloud), std::cend(cloud));
394 |
395 | ufo::insertPointCloud(map, cloud, viewpoint.translation, config.integration,
396 | config.propagate);
397 |
398 | if (config.printing.verbose) {
399 | timing[2] = config.integration.timing;
400 | timing.print(true, true, 2, 4);
401 | }
402 | }
403 | indicators::show_console_cursor(true);
404 | std::cout << "\033[0m\n[LOG] Step 3: Finished Processing data. Start saving map... " << std::endl;
405 | // bar.is_completed();
406 | if (!config.propagate) {
407 | timing[3].start("Propagate");
408 | map.propagateModified();
409 | timing[3].stop();
410 | }
411 |
412 | timing[4].start("Cluster");
413 | if (config.clustering.cluster) {
414 | cluster(map, config.clustering);
415 | }
416 | timing[4].stop();
417 |
418 | timing[5].start("Query");
419 | ufo::PointCloudColor cloud_static;
420 |
421 | for (auto& p : cloud_acc) {
422 | if (!map.seenFree(p))
423 | cloud_static.push_back(p);
424 | }
425 |
426 | timing[5].stop();
427 |
428 | timing[6].start("write");
429 | ufo::writePointCloudPCD(path / (config.output.filename + ".pcd"), cloud_static);
430 | timing[6].stop();
431 | timing.stop();
432 |
433 | timing[2] = config.integration.timing;
434 | timing.print(true, true, 2, 4);
435 | std::cout << "[LOG]: Finished! ^v^.. Clean output map with " << cloud_static.size() << " points save in " << path / (config.output.filename + ".pcd") << '\n';
436 | }
--------------------------------------------------------------------------------
/src/indicators.hpp:
--------------------------------------------------------------------------------
1 | // header from: https://github.com/p-ranav/indicators
2 | #ifndef INDICATORS_COLOR
3 | #define INDICATORS_COLOR
4 |
5 | namespace indicators {
6 | enum class Color { grey, red, green, yellow, blue, magenta, cyan, white, unspecified };
7 | }
8 |
9 | #endif
10 |
11 |
12 |
13 | #ifndef INDICATORS_FONT_STYLE
14 | #define INDICATORS_FONT_STYLE
15 |
16 | namespace indicators {
17 | enum class FontStyle { bold, dark, italic, underline, blink, reverse, concealed, crossed };
18 | }
19 |
20 | #endif
21 |
22 |
23 |
24 | #ifndef INDICATORS_PROGRESS_TYPE
25 | #define INDICATORS_PROGRESS_TYPE
26 |
27 | namespace indicators {
28 | enum class ProgressType { incremental, decremental };
29 | }
30 |
31 | #endif
32 |
33 | //!
34 | //! termcolor
35 | //! ~~~~~~~~~
36 | //!
37 | //! termcolor is a header-only c++ library for printing colored messages
38 | //! to the terminal. Written just for fun with a help of the Force.
39 | //!
40 | //! :copyright: (c) 2013 by Ihor Kalnytskyi
41 | //! :license: BSD, see LICENSE for details
42 | //!
43 |
44 | #ifndef TERMCOLOR_HPP_
45 | #define TERMCOLOR_HPP_
46 |
47 | #include
48 | #include
49 | #include
50 |
51 | // Detect target's platform and set some macros in order to wrap platform
52 | // specific code this library depends on.
53 | #if defined(_WIN32) || defined(_WIN64)
54 | # define TERMCOLOR_TARGET_WINDOWS
55 | #elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
56 | # define TERMCOLOR_TARGET_POSIX
57 | #endif
58 |
59 | // If implementation has not been explicitly set, try to choose one based on
60 | // target platform.
61 | #if !defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES) && !defined(TERMCOLOR_USE_WINDOWS_API) && !defined(TERMCOLOR_USE_NOOP)
62 | # if defined(TERMCOLOR_TARGET_POSIX)
63 | # define TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES
64 | # define TERMCOLOR_AUTODETECTED_IMPLEMENTATION
65 | # elif defined(TERMCOLOR_TARGET_WINDOWS)
66 | # define TERMCOLOR_USE_WINDOWS_API
67 | # define TERMCOLOR_AUTODETECTED_IMPLEMENTATION
68 | # endif
69 | #endif
70 |
71 | // These headers provide isatty()/fileno() functions, which are used for
72 | // testing whether a standard stream refers to the terminal.
73 | #if defined(TERMCOLOR_TARGET_POSIX)
74 | # include
75 | #elif defined(TERMCOLOR_TARGET_WINDOWS)
76 | #if defined(_MSC_VER)
77 | #if !defined(NOMINMAX)
78 | #define NOMINMAX
79 | #endif
80 | #endif
81 | # include
82 | # include
83 | #endif
84 |
85 |
86 | namespace termcolor
87 | {
88 | // Forward declaration of the `_internal` namespace.
89 | // All comments are below.
90 | namespace _internal
91 | {
92 | inline int colorize_index();
93 | inline FILE* get_standard_stream(const std::ostream& stream);
94 | inline bool is_colorized(std::ostream& stream);
95 | inline bool is_atty(const std::ostream& stream);
96 |
97 | #if defined(TERMCOLOR_TARGET_WINDOWS)
98 | inline void win_change_attributes(std::ostream& stream, int foreground, int background=-1);
99 | #endif
100 | }
101 |
102 | inline
103 | std::ostream& colorize(std::ostream& stream)
104 | {
105 | stream.iword(_internal::colorize_index()) = 1L;
106 | return stream;
107 | }
108 |
109 | inline
110 | std::ostream& nocolorize(std::ostream& stream)
111 | {
112 | stream.iword(_internal::colorize_index()) = 0L;
113 | return stream;
114 | }
115 |
116 | inline
117 | std::ostream& reset(std::ostream& stream)
118 | {
119 | if (_internal::is_colorized(stream))
120 | {
121 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
122 | stream << "\033[00m";
123 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
124 | _internal::win_change_attributes(stream, -1, -1);
125 | #endif
126 | }
127 | return stream;
128 | }
129 |
130 | inline
131 | std::ostream& bold(std::ostream& stream)
132 | {
133 | if (_internal::is_colorized(stream))
134 | {
135 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
136 | stream << "\033[1m";
137 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
138 | #endif
139 | }
140 | return stream;
141 | }
142 |
143 | inline
144 | std::ostream& dark(std::ostream& stream)
145 | {
146 | if (_internal::is_colorized(stream))
147 | {
148 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
149 | stream << "\033[2m";
150 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
151 | #endif
152 | }
153 | return stream;
154 | }
155 |
156 | inline
157 | std::ostream& italic(std::ostream& stream)
158 | {
159 | if (_internal::is_colorized(stream))
160 | {
161 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
162 | stream << "\033[3m";
163 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
164 | #endif
165 | }
166 | return stream;
167 | }
168 |
169 | inline
170 | std::ostream& underline(std::ostream& stream)
171 | {
172 | if (_internal::is_colorized(stream))
173 | {
174 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
175 | stream << "\033[4m";
176 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
177 | _internal::win_change_attributes(stream, -1, COMMON_LVB_UNDERSCORE);
178 | #endif
179 | }
180 | return stream;
181 | }
182 |
183 | inline
184 | std::ostream& blink(std::ostream& stream)
185 | {
186 | if (_internal::is_colorized(stream))
187 | {
188 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
189 | stream << "\033[5m";
190 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
191 | #endif
192 | }
193 | return stream;
194 | }
195 |
196 | inline
197 | std::ostream& reverse(std::ostream& stream)
198 | {
199 | if (_internal::is_colorized(stream))
200 | {
201 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
202 | stream << "\033[7m";
203 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
204 | #endif
205 | }
206 | return stream;
207 | }
208 |
209 | inline
210 | std::ostream& concealed(std::ostream& stream)
211 | {
212 | if (_internal::is_colorized(stream))
213 | {
214 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
215 | stream << "\033[8m";
216 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
217 | #endif
218 | }
219 | return stream;
220 | }
221 |
222 | inline
223 | std::ostream& crossed(std::ostream& stream)
224 | {
225 | if (_internal::is_colorized(stream))
226 | {
227 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
228 | stream << "\033[9m";
229 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
230 | #endif
231 | }
232 | return stream;
233 | }
234 |
235 | template inline
236 | std::ostream& color(std::ostream& stream)
237 | {
238 | if (_internal::is_colorized(stream))
239 | {
240 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
241 | char command[12];
242 | std::snprintf(command, sizeof(command), "\033[38;5;%dm", code);
243 | stream << command;
244 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
245 | #endif
246 | }
247 | return stream;
248 | }
249 |
250 | template inline
251 | std::ostream& on_color(std::ostream& stream)
252 | {
253 | if (_internal::is_colorized(stream))
254 | {
255 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
256 | char command[12];
257 | std::snprintf(command, sizeof(command), "\033[48;5;%dm", code);
258 | stream << command;
259 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
260 | #endif
261 | }
262 | return stream;
263 | }
264 |
265 | template inline
266 | std::ostream& color(std::ostream& stream)
267 | {
268 | if (_internal::is_colorized(stream))
269 | {
270 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
271 | char command[20];
272 | std::snprintf(command, sizeof(command), "\033[38;2;%d;%d;%dm", r, g, b);
273 | stream << command;
274 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
275 | #endif
276 | }
277 | return stream;
278 | }
279 |
280 | template inline
281 | std::ostream& on_color(std::ostream& stream)
282 | {
283 | if (_internal::is_colorized(stream))
284 | {
285 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
286 | char command[20];
287 | std::snprintf(command, sizeof(command), "\033[48;2;%d;%d;%dm", r, g, b);
288 | stream << command;
289 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
290 | #endif
291 | }
292 | return stream;
293 | }
294 |
295 | inline
296 | std::ostream& grey(std::ostream& stream)
297 | {
298 | if (_internal::is_colorized(stream))
299 | {
300 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
301 | stream << "\033[30m";
302 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
303 | _internal::win_change_attributes(stream,
304 | 0 // grey (black)
305 | );
306 | #endif
307 | }
308 | return stream;
309 | }
310 |
311 | inline
312 | std::ostream& red(std::ostream& stream)
313 | {
314 | if (_internal::is_colorized(stream))
315 | {
316 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
317 | stream << "\033[31m";
318 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
319 | _internal::win_change_attributes(stream,
320 | FOREGROUND_RED
321 | );
322 | #endif
323 | }
324 | return stream;
325 | }
326 |
327 | inline
328 | std::ostream& green(std::ostream& stream)
329 | {
330 | if (_internal::is_colorized(stream))
331 | {
332 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
333 | stream << "\033[32m";
334 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
335 | _internal::win_change_attributes(stream,
336 | FOREGROUND_GREEN
337 | );
338 | #endif
339 | }
340 | return stream;
341 | }
342 |
343 | inline
344 | std::ostream& yellow(std::ostream& stream)
345 | {
346 | if (_internal::is_colorized(stream))
347 | {
348 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
349 | stream << "\033[33m";
350 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
351 | _internal::win_change_attributes(stream,
352 | FOREGROUND_GREEN | FOREGROUND_RED
353 | );
354 | #endif
355 | }
356 | return stream;
357 | }
358 |
359 | inline
360 | std::ostream& blue(std::ostream& stream)
361 | {
362 | if (_internal::is_colorized(stream))
363 | {
364 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
365 | stream << "\033[34m";
366 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
367 | _internal::win_change_attributes(stream,
368 | FOREGROUND_BLUE
369 | );
370 | #endif
371 | }
372 | return stream;
373 | }
374 |
375 | inline
376 | std::ostream& magenta(std::ostream& stream)
377 | {
378 | if (_internal::is_colorized(stream))
379 | {
380 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
381 | stream << "\033[35m";
382 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
383 | _internal::win_change_attributes(stream,
384 | FOREGROUND_BLUE | FOREGROUND_RED
385 | );
386 | #endif
387 | }
388 | return stream;
389 | }
390 |
391 | inline
392 | std::ostream& cyan(std::ostream& stream)
393 | {
394 | if (_internal::is_colorized(stream))
395 | {
396 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
397 | stream << "\033[36m";
398 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
399 | _internal::win_change_attributes(stream,
400 | FOREGROUND_BLUE | FOREGROUND_GREEN
401 | );
402 | #endif
403 | }
404 | return stream;
405 | }
406 |
407 | inline
408 | std::ostream& white(std::ostream& stream)
409 | {
410 | if (_internal::is_colorized(stream))
411 | {
412 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
413 | stream << "\033[37m";
414 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
415 | _internal::win_change_attributes(stream,
416 | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
417 | );
418 | #endif
419 | }
420 | return stream;
421 | }
422 |
423 |
424 | inline
425 | std::ostream& bright_grey(std::ostream& stream)
426 | {
427 | if (_internal::is_colorized(stream))
428 | {
429 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
430 | stream << "\033[90m";
431 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
432 | _internal::win_change_attributes(stream,
433 | 0 | FOREGROUND_INTENSITY // grey (black)
434 | );
435 | #endif
436 | }
437 | return stream;
438 | }
439 |
440 | inline
441 | std::ostream& bright_red(std::ostream& stream)
442 | {
443 | if (_internal::is_colorized(stream))
444 | {
445 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
446 | stream << "\033[91m";
447 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
448 | _internal::win_change_attributes(stream,
449 | FOREGROUND_RED | FOREGROUND_INTENSITY
450 | );
451 | #endif
452 | }
453 | return stream;
454 | }
455 |
456 | inline
457 | std::ostream& bright_green(std::ostream& stream)
458 | {
459 | if (_internal::is_colorized(stream))
460 | {
461 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
462 | stream << "\033[92m";
463 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
464 | _internal::win_change_attributes(stream,
465 | FOREGROUND_GREEN | FOREGROUND_INTENSITY
466 | );
467 | #endif
468 | }
469 | return stream;
470 | }
471 |
472 | inline
473 | std::ostream& bright_yellow(std::ostream& stream)
474 | {
475 | if (_internal::is_colorized(stream))
476 | {
477 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
478 | stream << "\033[93m";
479 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
480 | _internal::win_change_attributes(stream,
481 | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY
482 | );
483 | #endif
484 | }
485 | return stream;
486 | }
487 |
488 | inline
489 | std::ostream& bright_blue(std::ostream& stream)
490 | {
491 | if (_internal::is_colorized(stream))
492 | {
493 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
494 | stream << "\033[94m";
495 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
496 | _internal::win_change_attributes(stream,
497 | FOREGROUND_BLUE | FOREGROUND_INTENSITY
498 | );
499 | #endif
500 | }
501 | return stream;
502 | }
503 |
504 | inline
505 | std::ostream& bright_magenta(std::ostream& stream)
506 | {
507 | if (_internal::is_colorized(stream))
508 | {
509 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
510 | stream << "\033[95m";
511 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
512 | _internal::win_change_attributes(stream,
513 | FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY
514 | );
515 | #endif
516 | }
517 | return stream;
518 | }
519 |
520 | inline
521 | std::ostream& bright_cyan(std::ostream& stream)
522 | {
523 | if (_internal::is_colorized(stream))
524 | {
525 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
526 | stream << "\033[96m";
527 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
528 | _internal::win_change_attributes(stream,
529 | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY
530 | );
531 | #endif
532 | }
533 | return stream;
534 | }
535 |
536 | inline
537 | std::ostream& bright_white(std::ostream& stream)
538 | {
539 | if (_internal::is_colorized(stream))
540 | {
541 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
542 | stream << "\033[97m";
543 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
544 | _internal::win_change_attributes(stream,
545 | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY
546 | );
547 | #endif
548 | }
549 | return stream;
550 | }
551 |
552 |
553 | inline
554 | std::ostream& on_grey(std::ostream& stream)
555 | {
556 | if (_internal::is_colorized(stream))
557 | {
558 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
559 | stream << "\033[40m";
560 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
561 | _internal::win_change_attributes(stream, -1,
562 | 0 // grey (black)
563 | );
564 | #endif
565 | }
566 | return stream;
567 | }
568 |
569 | inline
570 | std::ostream& on_red(std::ostream& stream)
571 | {
572 | if (_internal::is_colorized(stream))
573 | {
574 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
575 | stream << "\033[41m";
576 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
577 | _internal::win_change_attributes(stream, -1,
578 | BACKGROUND_RED
579 | );
580 | #endif
581 | }
582 | return stream;
583 | }
584 |
585 | inline
586 | std::ostream& on_green(std::ostream& stream)
587 | {
588 | if (_internal::is_colorized(stream))
589 | {
590 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
591 | stream << "\033[42m";
592 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
593 | _internal::win_change_attributes(stream, -1,
594 | BACKGROUND_GREEN
595 | );
596 | #endif
597 | }
598 | return stream;
599 | }
600 |
601 | inline
602 | std::ostream& on_yellow(std::ostream& stream)
603 | {
604 | if (_internal::is_colorized(stream))
605 | {
606 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
607 | stream << "\033[43m";
608 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
609 | _internal::win_change_attributes(stream, -1,
610 | BACKGROUND_GREEN | BACKGROUND_RED
611 | );
612 | #endif
613 | }
614 | return stream;
615 | }
616 |
617 | inline
618 | std::ostream& on_blue(std::ostream& stream)
619 | {
620 | if (_internal::is_colorized(stream))
621 | {
622 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
623 | stream << "\033[44m";
624 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
625 | _internal::win_change_attributes(stream, -1,
626 | BACKGROUND_BLUE
627 | );
628 | #endif
629 | }
630 | return stream;
631 | }
632 |
633 | inline
634 | std::ostream& on_magenta(std::ostream& stream)
635 | {
636 | if (_internal::is_colorized(stream))
637 | {
638 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
639 | stream << "\033[45m";
640 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
641 | _internal::win_change_attributes(stream, -1,
642 | BACKGROUND_BLUE | BACKGROUND_RED
643 | );
644 | #endif
645 | }
646 | return stream;
647 | }
648 |
649 | inline
650 | std::ostream& on_cyan(std::ostream& stream)
651 | {
652 | if (_internal::is_colorized(stream))
653 | {
654 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
655 | stream << "\033[46m";
656 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
657 | _internal::win_change_attributes(stream, -1,
658 | BACKGROUND_GREEN | BACKGROUND_BLUE
659 | );
660 | #endif
661 | }
662 | return stream;
663 | }
664 |
665 | inline
666 | std::ostream& on_white(std::ostream& stream)
667 | {
668 | if (_internal::is_colorized(stream))
669 | {
670 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
671 | stream << "\033[47m";
672 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
673 | _internal::win_change_attributes(stream, -1,
674 | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED
675 | );
676 | #endif
677 | }
678 |
679 | return stream;
680 | }
681 |
682 |
683 | inline
684 | std::ostream& on_bright_grey(std::ostream& stream)
685 | {
686 | if (_internal::is_colorized(stream))
687 | {
688 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
689 | stream << "\033[100m";
690 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
691 | _internal::win_change_attributes(stream, -1,
692 | 0 | BACKGROUND_INTENSITY // grey (black)
693 | );
694 | #endif
695 | }
696 | return stream;
697 | }
698 |
699 | inline
700 | std::ostream& on_bright_red(std::ostream& stream)
701 | {
702 | if (_internal::is_colorized(stream))
703 | {
704 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
705 | stream << "\033[101m";
706 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
707 | _internal::win_change_attributes(stream, -1,
708 | BACKGROUND_RED | BACKGROUND_INTENSITY
709 | );
710 | #endif
711 | }
712 | return stream;
713 | }
714 |
715 | inline
716 | std::ostream& on_bright_green(std::ostream& stream)
717 | {
718 | if (_internal::is_colorized(stream))
719 | {
720 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
721 | stream << "\033[102m";
722 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
723 | _internal::win_change_attributes(stream, -1,
724 | BACKGROUND_GREEN | BACKGROUND_INTENSITY
725 | );
726 | #endif
727 | }
728 | return stream;
729 | }
730 |
731 | inline
732 | std::ostream& on_bright_yellow(std::ostream& stream)
733 | {
734 | if (_internal::is_colorized(stream))
735 | {
736 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
737 | stream << "\033[103m";
738 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
739 | _internal::win_change_attributes(stream, -1,
740 | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY
741 | );
742 | #endif
743 | }
744 | return stream;
745 | }
746 |
747 | inline
748 | std::ostream& on_bright_blue(std::ostream& stream)
749 | {
750 | if (_internal::is_colorized(stream))
751 | {
752 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
753 | stream << "\033[104m";
754 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
755 | _internal::win_change_attributes(stream, -1,
756 | BACKGROUND_BLUE | BACKGROUND_INTENSITY
757 | );
758 | #endif
759 | }
760 | return stream;
761 | }
762 |
763 | inline
764 | std::ostream& on_bright_magenta(std::ostream& stream)
765 | {
766 | if (_internal::is_colorized(stream))
767 | {
768 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
769 | stream << "\033[105m";
770 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
771 | _internal::win_change_attributes(stream, -1,
772 | BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY
773 | );
774 | #endif
775 | }
776 | return stream;
777 | }
778 |
779 | inline
780 | std::ostream& on_bright_cyan(std::ostream& stream)
781 | {
782 | if (_internal::is_colorized(stream))
783 | {
784 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
785 | stream << "\033[106m";
786 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
787 | _internal::win_change_attributes(stream, -1,
788 | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY
789 | );
790 | #endif
791 | }
792 | return stream;
793 | }
794 |
795 | inline
796 | std::ostream& on_bright_white(std::ostream& stream)
797 | {
798 | if (_internal::is_colorized(stream))
799 | {
800 | #if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
801 | stream << "\033[107m";
802 | #elif defined(TERMCOLOR_USE_WINDOWS_API)
803 | _internal::win_change_attributes(stream, -1,
804 | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY
805 | );
806 | #endif
807 | }
808 |
809 | return stream;
810 | }
811 |
812 |
813 |
814 | //! Since C++ hasn't a way to hide something in the header from
815 | //! the outer access, I have to introduce this namespace which
816 | //! is used for internal purpose and should't be access from
817 | //! the user code.
818 | namespace _internal
819 | {
820 | // An index to be used to access a private storage of I/O streams. See
821 | // colorize / nocolorize I/O manipulators for details. Due to the fact
822 | // that static variables ain't shared between translation units, inline
823 | // function with local static variable is used to do the trick and share
824 | // the variable value between translation units.
825 | inline int colorize_index()
826 | {
827 | static int colorize_index = std::ios_base::xalloc();
828 | return colorize_index;
829 | }
830 |
831 | //! Since C++ hasn't a true way to extract stream handler
832 | //! from the a given `std::ostream` object, I have to write
833 | //! this kind of hack.
834 | inline
835 | FILE* get_standard_stream(const std::ostream& stream)
836 | {
837 | if (&stream == &std::cout)
838 | return stdout;
839 | else if ((&stream == &std::cerr) || (&stream == &std::clog))
840 | return stderr;
841 |
842 | return nullptr;
843 | }
844 |
845 | // Say whether a given stream should be colorized or not. It's always
846 | // true for ATTY streams and may be true for streams marked with
847 | // colorize flag.
848 | inline
849 | bool is_colorized(std::ostream& stream)
850 | {
851 | return is_atty(stream) || static_cast(stream.iword(colorize_index()));
852 | }
853 |
854 | //! Test whether a given `std::ostream` object refers to
855 | //! a terminal.
856 | inline
857 | bool is_atty(const std::ostream& stream)
858 | {
859 | FILE* std_stream = get_standard_stream(stream);
860 |
861 | // Unfortunately, fileno() ends with segmentation fault
862 | // if invalid file descriptor is passed. So we need to
863 | // handle this case gracefully and assume it's not a tty
864 | // if standard stream is not detected, and 0 is returned.
865 | if (!std_stream)
866 | return false;
867 |
868 | #if defined(TERMCOLOR_TARGET_POSIX)
869 | return ::isatty(fileno(std_stream));
870 | #elif defined(TERMCOLOR_TARGET_WINDOWS)
871 | return ::_isatty(_fileno(std_stream));
872 | #else
873 | return false;
874 | #endif
875 | }
876 |
877 | #if defined(TERMCOLOR_TARGET_WINDOWS)
878 | //! Change Windows Terminal colors attribute. If some
879 | //! parameter is `-1` then attribute won't changed.
880 | inline void win_change_attributes(std::ostream& stream, int foreground, int background)
881 | {
882 | // yeah, i know.. it's ugly, it's windows.
883 | static WORD defaultAttributes = 0;
884 |
885 | // Windows doesn't have ANSI escape sequences and so we use special
886 | // API to change Terminal output color. That means we can't
887 | // manipulate colors by means of "std::stringstream" and hence
888 | // should do nothing in this case.
889 | if (!_internal::is_atty(stream))
890 | return;
891 |
892 | // get terminal handle
893 | HANDLE hTerminal = INVALID_HANDLE_VALUE;
894 | if (&stream == &std::cout)
895 | hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
896 | else if (&stream == &std::cerr)
897 | hTerminal = GetStdHandle(STD_ERROR_HANDLE);
898 |
899 | // save default terminal attributes if it unsaved
900 | if (!defaultAttributes)
901 | {
902 | CONSOLE_SCREEN_BUFFER_INFO info;
903 | if (!GetConsoleScreenBufferInfo(hTerminal, &info))
904 | return;
905 | defaultAttributes = info.wAttributes;
906 | }
907 |
908 | // restore all default settings
909 | if (foreground == -1 && background == -1)
910 | {
911 | SetConsoleTextAttribute(hTerminal, defaultAttributes);
912 | return;
913 | }
914 |
915 | // get current settings
916 | CONSOLE_SCREEN_BUFFER_INFO info;
917 | if (!GetConsoleScreenBufferInfo(hTerminal, &info))
918 | return;
919 |
920 | if (foreground != -1)
921 | {
922 | info.wAttributes &= ~(info.wAttributes & 0x0F);
923 | info.wAttributes |= static_cast(foreground);
924 | }
925 |
926 | if (background != -1)
927 | {
928 | info.wAttributes &= ~(info.wAttributes & 0xF0);
929 | info.wAttributes |= static_cast(background);
930 | }
931 |
932 | SetConsoleTextAttribute(hTerminal, info.wAttributes);
933 | }
934 | #endif // TERMCOLOR_TARGET_WINDOWS
935 |
936 | } // namespace _internal
937 |
938 | } // namespace termcolor
939 |
940 |
941 | #undef TERMCOLOR_TARGET_POSIX
942 | #undef TERMCOLOR_TARGET_WINDOWS
943 |
944 | #if defined(TERMCOLOR_AUTODETECTED_IMPLEMENTATION)
945 | # undef TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES
946 | # undef TERMCOLOR_USE_WINDOWS_API
947 | #endif
948 |
949 | #endif // TERMCOLOR_HPP_
950 |
951 |
952 |
953 | #ifndef INDICATORS_TERMINAL_SIZE
954 | #define INDICATORS_TERMINAL_SIZE
955 | #include
956 |
957 |
958 | #if defined(_WIN32)
959 | #include
960 |
961 | namespace indicators {
962 |
963 | static inline std::pair terminal_size() {
964 | CONSOLE_SCREEN_BUFFER_INFO csbi;
965 | int cols, rows;
966 | GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
967 | cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
968 | rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
969 | return {static_cast(rows), static_cast(cols)};
970 | }
971 |
972 | static inline size_t terminal_width() { return terminal_size().second; }
973 |
974 | } // namespace indicators
975 |
976 | #else
977 |
978 | #include //ioctl() and TIOCGWINSZ
979 | #include // for STDOUT_FILENO
980 |
981 | namespace indicators {
982 |
983 | static inline std::pair terminal_size() {
984 | struct winsize size{};
985 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &size);
986 | return {static_cast(size.ws_row), static_cast(size.ws_col)};
987 | }
988 |
989 | static inline size_t terminal_width() { return terminal_size().second; }
990 |
991 | } // namespace indicators
992 |
993 | #endif
994 |
995 | #endif
996 |
997 |
998 | /*
999 | Activity Indicators for Modern C++
1000 | https://github.com/p-ranav/indicators
1001 |
1002 | Licensed under the MIT License .
1003 | SPDX-License-Identifier: MIT
1004 | Copyright (c) 2019 Dawid Pilarski .
1005 |
1006 | Permission is hereby granted, free of charge, to any person obtaining a copy
1007 | of this software and associated documentation files (the "Software"), to deal
1008 | in the Software without restriction, including without limitation the rights
1009 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010 | copies of the Software, and to permit persons to whom the Software is
1011 | furnished to do so, subject to the following conditions:
1012 |
1013 | The above copyright notice and this permission notice shall be included in all
1014 | copies or substantial portions of the Software.
1015 |
1016 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1017 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1018 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1019 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1020 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1021 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1022 | SOFTWARE.
1023 | */
1024 | #ifndef INDICATORS_SETTING
1025 | #define INDICATORS_SETTING
1026 |
1027 | #include
1028 | // #include
1029 | // #include
1030 | // #include
1031 | #include
1032 | #include
1033 | #include
1034 | #include
1035 | #include
1036 |
1037 | namespace indicators {
1038 |
1039 | namespace details {
1040 |
1041 | template struct if_else;
1042 |
1043 | template <> struct if_else { using type = std::true_type; };
1044 |
1045 | template <> struct if_else { using type = std::false_type; };
1046 |
1047 | template struct if_else_type;
1048 |
1049 | template struct if_else_type {
1050 | using type = True;
1051 | };
1052 |
1053 | template struct if_else_type {
1054 | using type = False;
1055 | };
1056 |
1057 | template struct conjuction;
1058 |
1059 | template <> struct conjuction<> : std::true_type {};
1060 |
1061 | template
1062 | struct conjuction
1063 | : if_else_type>::type {};
1064 |
1065 | template struct disjunction;
1066 |
1067 | template <> struct disjunction<> : std::false_type {};
1068 |
1069 | template
1070 | struct disjunction
1071 | : if_else_type>::type {};
1072 |
1073 | enum class ProgressBarOption {
1074 | bar_width = 0,
1075 | prefix_text,
1076 | postfix_text,
1077 | start,
1078 | end,
1079 | fill,
1080 | lead,
1081 | remainder,
1082 | max_postfix_text_len,
1083 | completed,
1084 | show_percentage,
1085 | show_elapsed_time,
1086 | show_remaining_time,
1087 | saved_start_time,
1088 | foreground_color,
1089 | spinner_show,
1090 | spinner_states,
1091 | font_styles,
1092 | hide_bar_when_complete,
1093 | min_progress,
1094 | max_progress,
1095 | progress_type,
1096 | stream
1097 | };
1098 |
1099 | template struct Setting {
1100 | template ::value>::type>
1102 | explicit Setting(Args &&... args) : value(std::forward(args)...) {}
1103 | Setting(const Setting &) = default;
1104 | Setting(Setting &&) = default;
1105 |
1106 | static constexpr auto id = Id;
1107 | using type = T;
1108 |
1109 | T value{};
1110 | };
1111 |
1112 | template struct is_setting : std::false_type {};
1113 |
1114 | template struct is_setting> : std::true_type {};
1115 |
1116 | template
1117 | struct are_settings : if_else...>::value>::type {};
1118 |
1119 | template <> struct are_settings<> : std::true_type {};
1120 |
1121 | template struct is_setting_from_tuple;
1122 |
1123 | template struct is_setting_from_tuple> : std::true_type {};
1124 |
1125 | template
1126 | struct is_setting_from_tuple>
1127 | : if_else...>::value>::type {};
1128 |
1129 | template
1130 | struct are_settings_from_tuple
1131 | : if_else...>::value>::type {};
1132 |
1133 | template struct always_true { static constexpr auto value = true; };
1134 |
1135 | template Default &&get_impl(Default &&def) {
1136 | return std::forward(def);
1137 | }
1138 |
1139 | template
1140 | auto get_impl(Default && /*def*/, T &&first, Args &&... /*tail*/) ->
1141 | typename std::enable_if<(std::decay::type::id == Id),
1142 | decltype(std::forward(first))>::type {
1143 | return std::forward(first);
1144 | }
1145 |
1146 | template
1147 | auto get_impl(Default &&def, T && /*first*/, Args &&... tail) ->
1148 | typename std::enable_if<(std::decay::type::id != Id),
1149 | decltype(get_impl(std::forward(def),
1150 | std::forward(tail)...))>::type {
1151 | return get_impl(std::forward(def), std::forward(tail)...);
1152 | }
1153 |
1154 | template ::value, void>::type>
1156 | auto get(Default &&def, Args &&... args)
1157 | -> decltype(details::get_impl(std::forward(def), std::forward(args)...)) {
1158 | return details::get_impl(std::forward(def), std::forward(args)...);
1159 | }
1160 |
1161 | template using StringSetting = Setting;
1162 |
1163 | template using IntegerSetting = Setting;
1164 |
1165 | template using BooleanSetting = Setting;
1166 |
1167 | template struct option_idx;
1168 |
1169 | template
1170 | struct option_idx, counter>
1171 | : if_else_type<(Id == T::id), std::integral_constant,
1172 | option_idx, counter + 1>>::type {};
1173 |
1174 | template struct option_idx, counter> {
1175 | static_assert(always_true<(ProgressBarOption)Id>::value, "No such option was found");
1176 | };
1177 |
1178 | template
1179 | auto get_value(Settings &&settings)
1180 | -> decltype((std::get::type>::value>(
1181 | std::declval()))) {
1182 | return std::get::type>::value>(
1183 | std::forward(settings));
1184 | }
1185 |
1186 | } // namespace details
1187 |
1188 | namespace option {
1189 | using BarWidth = details::IntegerSetting;
1190 | using PrefixText = details::StringSetting;
1191 | using PostfixText = details::StringSetting;
1192 | using Start = details::StringSetting;
1193 | using End = details::StringSetting;
1194 | using Fill = details::StringSetting;
1195 | using Lead = details::StringSetting;
1196 | using Remainder = details::StringSetting;
1197 | using MaxPostfixTextLen = details::IntegerSetting;
1198 | using Completed = details::BooleanSetting;
1199 | using ShowPercentage = details::BooleanSetting;
1200 | using ShowElapsedTime = details::BooleanSetting;
1201 | using ShowRemainingTime = details::BooleanSetting;
1202 | using SavedStartTime = details::BooleanSetting;
1203 | using ForegroundColor = details::Setting;
1204 | using ShowSpinner = details::BooleanSetting;
1205 | using SpinnerStates =
1206 | details::Setting, details::ProgressBarOption::spinner_states>;
1207 | using HideBarWhenComplete =
1208 | details::BooleanSetting;
1209 | using FontStyles =
1210 | details::Setting, details::ProgressBarOption::font_styles>;
1211 | using MinProgress = details::IntegerSetting;
1212 | using MaxProgress = details::IntegerSetting;
1213 | using ProgressType = details::Setting;
1214 | using Stream = details::Setting;
1215 | } // namespace option
1216 | } // namespace indicators
1217 |
1218 | #endif
1219 |
1220 |
1221 | #ifndef INDICATORS_CURSOR_CONTROL
1222 | #define INDICATORS_CURSOR_CONTROL
1223 |
1224 | #if defined(_MSC_VER)
1225 | #if !defined(NOMINMAX)
1226 | #define NOMINMAX
1227 | #endif
1228 | #include
1229 | #include
1230 | #else
1231 | #include
1232 | #endif
1233 |
1234 | namespace indicators {
1235 |
1236 | #if defined(_MSC_VER)
1237 |
1238 | static inline void show_console_cursor(bool const show) {
1239 | HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
1240 |
1241 | CONSOLE_CURSOR_INFO cursorInfo;
1242 |
1243 | GetConsoleCursorInfo(out, &cursorInfo);
1244 | cursorInfo.bVisible = show; // set the cursor visibility
1245 | SetConsoleCursorInfo(out, &cursorInfo);
1246 | }
1247 |
1248 | static inline void erase_line() {
1249 | auto hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
1250 | if (!hStdout)
1251 | return;
1252 |
1253 | CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
1254 | GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
1255 |
1256 | COORD cursor;
1257 |
1258 | cursor.X = 0;
1259 | cursor.Y = csbiInfo.dwCursorPosition.Y;
1260 |
1261 | DWORD count = 0;
1262 |
1263 | FillConsoleOutputCharacterA(hStdout, ' ', csbiInfo.dwSize.X, cursor, &count);
1264 |
1265 | FillConsoleOutputAttribute(hStdout, csbiInfo.wAttributes, csbiInfo.dwSize.X,
1266 | cursor, &count);
1267 |
1268 | SetConsoleCursorPosition(hStdout, cursor);
1269 | }
1270 |
1271 | #else
1272 |
1273 | static inline void show_console_cursor(bool const show) {
1274 | std::fputs(show ? "\033[?25h" : "\033[?25l", stdout);
1275 | }
1276 |
1277 | static inline void erase_line() {
1278 | std::fputs("\r\033[K", stdout);
1279 | }
1280 |
1281 | #endif
1282 |
1283 | } // namespace indicators
1284 |
1285 | #endif
1286 |
1287 |
1288 | #ifndef INDICATORS_CURSOR_MOVEMENT
1289 | #define INDICATORS_CURSOR_MOVEMENT
1290 |
1291 | #if defined(_MSC_VER)
1292 | #if !defined(NOMINMAX)
1293 | #define NOMINMAX
1294 | #endif
1295 | #include
1296 | #include
1297 | #else
1298 | #include
1299 | #endif
1300 |
1301 | namespace indicators {
1302 |
1303 | #ifdef _MSC_VER
1304 |
1305 | static inline void move(int x, int y) {
1306 | auto hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
1307 | if (!hStdout)
1308 | return;
1309 |
1310 | CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
1311 | GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
1312 |
1313 | COORD cursor;
1314 |
1315 | cursor.X = csbiInfo.dwCursorPosition.X + x;
1316 | cursor.Y = csbiInfo.dwCursorPosition.Y + y;
1317 | SetConsoleCursorPosition(hStdout, cursor);
1318 | }
1319 |
1320 | static inline void move_up(int lines) { move(0, -lines); }
1321 | static inline void move_down(int lines) { move(0, -lines); }
1322 | static inline void move_right(int cols) { move(cols, 0); }
1323 | static inline void move_left(int cols) { move(-cols, 0); }
1324 |
1325 | #else
1326 |
1327 | static inline void move_up(int lines) { std::cout << "\033[" << lines << "A"; }
1328 | static inline void move_down(int lines) { std::cout << "\033[" << lines << "B"; }
1329 | static inline void move_right(int cols) { std::cout << "\033[" << cols << "C"; }
1330 | static inline void move_left(int cols) { std::cout << "\033[" << cols << "D"; }
1331 |
1332 | #endif
1333 |
1334 | } // namespace indicators
1335 |
1336 | #endif
1337 |
1338 |
1339 | #ifndef INDICATORS_STREAM_HELPER
1340 | #define INDICATORS_STREAM_HELPER
1341 |
1342 | // #include
1343 | #ifndef INDICATORS_DISPLAY_WIDTH
1344 | #define INDICATORS_DISPLAY_WIDTH
1345 |
1346 | #include
1347 | #include
1348 | #include
1349 | #include
1350 | #include
1351 |
1352 | namespace unicode {
1353 |
1354 | namespace details {
1355 |
1356 | /*
1357 | * This is an implementation of wcwidth() and wcswidth() (defined in
1358 | * IEEE Std 1002.1-2001) for Unicode.
1359 | *
1360 | * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
1361 | * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
1362 | *
1363 | * In fixed-width output devices, Latin characters all occupy a single
1364 | * "cell" position of equal width, whereas ideographic CJK characters
1365 | * occupy two such cells. Interoperability between terminal-line
1366 | * applications and (teletype-style) character terminals using the
1367 | * UTF-8 encoding requires agreement on which character should advance
1368 | * the cursor by how many cell positions. No established formal
1369 | * standards exist at present on which Unicode character shall occupy
1370 | * how many cell positions on character terminals. These routines are
1371 | * a first attempt of defining such behavior based on simple rules
1372 | * applied to data provided by the Unicode Consortium.
1373 | *
1374 | * For some graphical characters, the Unicode standard explicitly
1375 | * defines a character-cell width via the definition of the East Asian
1376 | * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
1377 | * In all these cases, there is no ambiguity about which width a
1378 | * terminal shall use. For characters in the East Asian Ambiguous (A)
1379 | * class, the width choice depends purely on a preference of backward
1380 | * compatibility with either historic CJK or Western practice.
1381 | * Choosing single-width for these characters is easy to justify as
1382 | * the appropriate long-term solution, as the CJK practice of
1383 | * displaying these characters as double-width comes from historic
1384 | * implementation simplicity (8-bit encoded characters were displayed
1385 | * single-width and 16-bit ones double-width, even for Greek,
1386 | * Cyrillic, etc.) and not any typographic considerations.
1387 | *
1388 | * Much less clear is the choice of width for the Not East Asian
1389 | * (Neutral) class. Existing practice does not dictate a width for any
1390 | * of these characters. It would nevertheless make sense
1391 | * typographically to allocate two character cells to characters such
1392 | * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
1393 | * represented adequately with a single-width glyph. The following
1394 | * routines at present merely assign a single-cell width to all
1395 | * neutral characters, in the interest of simplicity. This is not
1396 | * entirely satisfactory and should be reconsidered before
1397 | * establishing a formal standard in this area. At the moment, the
1398 | * decision which Not East Asian (Neutral) characters should be
1399 | * represented by double-width glyphs cannot yet be answered by
1400 | * applying a simple rule from the Unicode database content. Setting
1401 | * up a proper standard for the behavior of UTF-8 character terminals
1402 | * will require a careful analysis not only of each Unicode character,
1403 | * but also of each presentation form, something the author of these
1404 | * routines has avoided to do so far.
1405 | *
1406 | * http://www.unicode.org/unicode/reports/tr11/
1407 | *
1408 | * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
1409 | *
1410 | * Permission to use, copy, modify, and distribute this software
1411 | * for any purpose and without fee is hereby granted. The author
1412 | * disclaims all warranties with regard to this software.
1413 | *
1414 | * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
1415 | */
1416 |
1417 | struct interval {
1418 | int first;
1419 | int last;
1420 | };
1421 |
1422 | /* auxiliary function for binary search in interval table */
1423 | static inline int bisearch(wchar_t ucs, const struct interval *table, int max) {
1424 | int min = 0;
1425 | int mid;
1426 |
1427 | if (ucs < table[0].first || ucs > table[max].last)
1428 | return 0;
1429 | while (max >= min) {
1430 | mid = (min + max) / 2;
1431 | if (ucs > table[mid].last)
1432 | min = mid + 1;
1433 | else if (ucs < table[mid].first)
1434 | max = mid - 1;
1435 | else
1436 | return 1;
1437 | }
1438 |
1439 | return 0;
1440 | }
1441 |
1442 | /* The following two functions define the column width of an ISO 10646
1443 | * character as follows:
1444 | *
1445 | * - The null character (U+0000) has a column width of 0.
1446 | *
1447 | * - Other C0/C1 control characters and DEL will lead to a return
1448 | * value of -1.
1449 | *
1450 | * - Non-spacing and enclosing combining characters (general
1451 | * category code Mn or Me in the Unicode database) have a
1452 | * column width of 0.
1453 | *
1454 | * - SOFT HYPHEN (U+00AD) has a column width of 1.
1455 | *
1456 | * - Other format characters (general category code Cf in the Unicode
1457 | * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
1458 | *
1459 | * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
1460 | * have a column width of 0.
1461 | *
1462 | * - Spacing characters in the East Asian Wide (W) or East Asian
1463 | * Full-width (F) category as defined in Unicode Technical
1464 | * Report #11 have a column width of 2.
1465 | *
1466 | * - All remaining characters (including all printable
1467 | * ISO 8859-1 and WGL4 characters, Unicode control characters,
1468 | * etc.) have a column width of 1.
1469 | *
1470 | * This implementation assumes that wchar_t characters are encoded
1471 | * in ISO 10646.
1472 | */
1473 |
1474 | static inline int mk_wcwidth(wchar_t ucs) {
1475 | /* sorted list of non-overlapping intervals of non-spacing characters */
1476 | /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
1477 | static const struct interval combining[] = {
1478 | {0x0300, 0x036F}, {0x0483, 0x0486}, {0x0488, 0x0489},
1479 | {0x0591, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2},
1480 | {0x05C4, 0x05C5}, {0x05C7, 0x05C7}, {0x0600, 0x0603},
1481 | {0x0610, 0x0615}, {0x064B, 0x065E}, {0x0670, 0x0670},
1482 | {0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED},
1483 | {0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A},
1484 | {0x07A6, 0x07B0}, {0x07EB, 0x07F3}, {0x0901, 0x0902},
1485 | {0x093C, 0x093C}, {0x0941, 0x0948}, {0x094D, 0x094D},
1486 | {0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0981},
1487 | {0x09BC, 0x09BC}, {0x09C1, 0x09C4}, {0x09CD, 0x09CD},
1488 | {0x09E2, 0x09E3}, {0x0A01, 0x0A02}, {0x0A3C, 0x0A3C},
1489 | {0x0A41, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D},
1490 | {0x0A70, 0x0A71}, {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC},
1491 | {0x0AC1, 0x0AC5}, {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD},
1492 | {0x0AE2, 0x0AE3}, {0x0B01, 0x0B01}, {0x0B3C, 0x0B3C},
1493 | {0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, {0x0B4D, 0x0B4D},
1494 | {0x0B56, 0x0B56}, {0x0B82, 0x0B82}, {0x0BC0, 0x0BC0},
1495 | {0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, {0x0C46, 0x0C48},
1496 | {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0CBC, 0x0CBC},
1497 | {0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD},
1498 | {0x0CE2, 0x0CE3}, {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D},
1499 | {0x0DCA, 0x0DCA}, {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6},
1500 | {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E},
1501 | {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC},
1502 | {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35},
1503 | {0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F71, 0x0F7E},
1504 | {0x0F80, 0x0F84}, {0x0F86, 0x0F87}, {0x0F90, 0x0F97},
1505 | {0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102D, 0x1030},
1506 | {0x1032, 0x1032}, {0x1036, 0x1037}, {0x1039, 0x1039},
1507 | {0x1058, 0x1059}, {0x1160, 0x11FF}, {0x135F, 0x135F},
1508 | {0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753},
1509 | {0x1772, 0x1773}, {0x17B4, 0x17B5}, {0x17B7, 0x17BD},
1510 | {0x17C6, 0x17C6}, {0x17C9, 0x17D3}, {0x17DD, 0x17DD},
1511 | {0x180B, 0x180D}, {0x18A9, 0x18A9}, {0x1920, 0x1922},
1512 | {0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193B},
1513 | {0x1A17, 0x1A18}, {0x1B00, 0x1B03}, {0x1B34, 0x1B34},
1514 | {0x1B36, 0x1B3A}, {0x1B3C, 0x1B3C}, {0x1B42, 0x1B42},
1515 | {0x1B6B, 0x1B73}, {0x1DC0, 0x1DCA}, {0x1DFE, 0x1DFF},
1516 | {0x200B, 0x200F}, {0x202A, 0x202E}, {0x2060, 0x2063},
1517 | {0x206A, 0x206F}, {0x20D0, 0x20EF}, {0x302A, 0x302F},
1518 | {0x3099, 0x309A}, {0xA806, 0xA806}, {0xA80B, 0xA80B},
1519 | {0xA825, 0xA826}, {0xFB1E, 0xFB1E}, {0xFE00, 0xFE0F},
1520 | {0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB},
1521 | {0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F},
1522 | {0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x1D167, 0x1D169},
1523 | {0x1D173, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD},
1524 | {0x1D242, 0x1D244}, {0xE0001, 0xE0001}, {0xE0020, 0xE007F},
1525 | {0xE0100, 0xE01EF}};
1526 |
1527 | /* test for 8-bit control characters */
1528 | if (ucs == 0)
1529 | return 0;
1530 | if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
1531 | return -1;
1532 |
1533 | /* binary search in table of non-spacing characters */
1534 | if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1))
1535 | return 0;
1536 |
1537 | /* if we arrive here, ucs is not a combining or C0/C1 control character */
1538 |
1539 | return 1 +
1540 | (ucs >= 0x1100 &&
1541 | (ucs <= 0x115f || /* Hangul Jamo init. consonants */
1542 | ucs == 0x2329 || ucs == 0x232a ||
1543 | (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */
1544 | (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
1545 | (ucs >= 0xf900 &&
1546 | ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
1547 | (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
1548 | (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
1549 | (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
1550 | (ucs >= 0xffe0 && ucs <= 0xffe6) ||
1551 | (ucs >= 0x20000 && ucs <= 0x2fffd) ||
1552 | (ucs >= 0x30000 && ucs <= 0x3fffd)));
1553 | }
1554 |
1555 | static inline int mk_wcswidth(const wchar_t *pwcs, size_t n) {
1556 | int w, width = 0;
1557 |
1558 | for (; *pwcs && n-- > 0; pwcs++)
1559 | if ((w = mk_wcwidth(*pwcs)) < 0)
1560 | return -1;
1561 | else
1562 | width += w;
1563 |
1564 | return width;
1565 | }
1566 |
1567 | /*
1568 | * The following functions are the same as mk_wcwidth() and
1569 | * mk_wcswidth(), except that spacing characters in the East Asian
1570 | * Ambiguous (A) category as defined in Unicode Technical Report #11
1571 | * have a column width of 2. This variant might be useful for users of
1572 | * CJK legacy encodings who want to migrate to UCS without changing
1573 | * the traditional terminal character-width behaviour. It is not
1574 | * otherwise recommended for general use.
1575 | */
1576 | static inline int mk_wcwidth_cjk(wchar_t ucs) {
1577 | /* sorted list of non-overlapping intervals of East Asian Ambiguous
1578 | * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
1579 | static const struct interval ambiguous[] = {
1580 | {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8},
1581 | {0x00AA, 0x00AA}, {0x00AE, 0x00AE}, {0x00B0, 0x00B4},
1582 | {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6},
1583 | {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1},
1584 | {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED},
1585 | {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA},
1586 | {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101},
1587 | {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B},
1588 | {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133},
1589 | {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144},
1590 | {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153},
1591 | {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE},
1592 | {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4},
1593 | {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA},
1594 | {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261},
1595 | {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB},
1596 | {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB},
1597 | {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0391, 0x03A1},
1598 | {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, {0x03C3, 0x03C9},
1599 | {0x0401, 0x0401}, {0x0410, 0x044F}, {0x0451, 0x0451},
1600 | {0x2010, 0x2010}, {0x2013, 0x2016}, {0x2018, 0x2019},
1601 | {0x201C, 0x201D}, {0x2020, 0x2022}, {0x2024, 0x2027},
1602 | {0x2030, 0x2030}, {0x2032, 0x2033}, {0x2035, 0x2035},
1603 | {0x203B, 0x203B}, {0x203E, 0x203E}, {0x2074, 0x2074},
1604 | {0x207F, 0x207F}, {0x2081, 0x2084}, {0x20AC, 0x20AC},
1605 | {0x2103, 0x2103}, {0x2105, 0x2105}, {0x2109, 0x2109},
1606 | {0x2113, 0x2113}, {0x2116, 0x2116}, {0x2121, 0x2122},
1607 | {0x2126, 0x2126}, {0x212B, 0x212B}, {0x2153, 0x2154},
1608 | {0x215B, 0x215E}, {0x2160, 0x216B}, {0x2170, 0x2179},
1609 | {0x2190, 0x2199}, {0x21B8, 0x21B9}, {0x21D2, 0x21D2},
1610 | {0x21D4, 0x21D4}, {0x21E7, 0x21E7}, {0x2200, 0x2200},
1611 | {0x2202, 0x2203}, {0x2207, 0x2208}, {0x220B, 0x220B},
1612 | {0x220F, 0x220F}, {0x2211, 0x2211}, {0x2215, 0x2215},
1613 | {0x221A, 0x221A}, {0x221D, 0x2220}, {0x2223, 0x2223},
1614 | {0x2225, 0x2225}, {0x2227, 0x222C}, {0x222E, 0x222E},
1615 | {0x2234, 0x2237}, {0x223C, 0x223D}, {0x2248, 0x2248},
1616 | {0x224C, 0x224C}, {0x2252, 0x2252}, {0x2260, 0x2261},
1617 | {0x2264, 0x2267}, {0x226A, 0x226B}, {0x226E, 0x226F},
1618 | {0x2282, 0x2283}, {0x2286, 0x2287}, {0x2295, 0x2295},
1619 | {0x2299, 0x2299}, {0x22A5, 0x22A5}, {0x22BF, 0x22BF},
1620 | {0x2312, 0x2312}, {0x2460, 0x24E9}, {0x24EB, 0x254B},
1621 | {0x2550, 0x2573}, {0x2580, 0x258F}, {0x2592, 0x2595},
1622 | {0x25A0, 0x25A1}, {0x25A3, 0x25A9}, {0x25B2, 0x25B3},
1623 | {0x25B6, 0x25B7}, {0x25BC, 0x25BD}, {0x25C0, 0x25C1},
1624 | {0x25C6, 0x25C8}, {0x25CB, 0x25CB}, {0x25CE, 0x25D1},
1625 | {0x25E2, 0x25E5}, {0x25EF, 0x25EF}, {0x2605, 0x2606},
1626 | {0x2609, 0x2609}, {0x260E, 0x260F}, {0x2614, 0x2615},
1627 | {0x261C, 0x261C}, {0x261E, 0x261E}, {0x2640, 0x2640},
1628 | {0x2642, 0x2642}, {0x2660, 0x2661}, {0x2663, 0x2665},
1629 | {0x2667, 0x266A}, {0x266C, 0x266D}, {0x266F, 0x266F},
1630 | {0x273D, 0x273D}, {0x2776, 0x277F}, {0xE000, 0xF8FF},
1631 | {0xFFFD, 0xFFFD}, {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}};
1632 |
1633 | /* binary search in table of non-spacing characters */
1634 | if (bisearch(ucs, ambiguous, sizeof(ambiguous) / sizeof(struct interval) - 1))
1635 | return 2;
1636 |
1637 | return mk_wcwidth(ucs);
1638 | }
1639 |
1640 | static inline int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) {
1641 | int w, width = 0;
1642 |
1643 | for (; *pwcs && n-- > 0; pwcs++)
1644 | if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
1645 | return -1;
1646 | else
1647 | width += w;
1648 |
1649 | return width;
1650 | }
1651 |
1652 | // convert UTF-8 string to wstring
1653 | #ifdef _MSC_VER
1654 | static inline std::wstring utf8_decode(const std::string& s) {
1655 | auto r = setlocale(LC_ALL, "");
1656 | std::string curLocale;
1657 | if (r)
1658 | curLocale = r;
1659 | const char* _Source = s.c_str();
1660 | size_t _Dsize = std::strlen(_Source) + 1;
1661 | wchar_t* _Dest = new wchar_t[_Dsize];
1662 | size_t _Osize;
1663 | mbstowcs_s(&_Osize, _Dest, _Dsize, _Source, _Dsize);
1664 | std::wstring result = _Dest;
1665 | delete[] _Dest;
1666 | setlocale(LC_ALL, curLocale.c_str());
1667 | return result;
1668 | }
1669 | #else
1670 | static inline std::wstring utf8_decode(const std::string& s) {
1671 | auto r = setlocale(LC_ALL, "");
1672 | std::string curLocale;
1673 | if (r)
1674 | curLocale = r;
1675 | const char* _Source = s.c_str();
1676 | size_t _Dsize = mbstowcs(NULL, _Source, 0) + 1;
1677 | wchar_t* _Dest = new wchar_t[_Dsize];
1678 | wmemset(_Dest, 0, _Dsize);
1679 | mbstowcs(_Dest, _Source, _Dsize);
1680 | std::wstring result = _Dest;
1681 | delete[] _Dest;
1682 | setlocale(LC_ALL, curLocale.c_str());
1683 | return result;
1684 | }
1685 | #endif
1686 |
1687 | } // namespace details
1688 |
1689 | static inline int display_width(const std::string &input) {
1690 | using namespace unicode::details;
1691 | return mk_wcswidth(utf8_decode(input).c_str(), input.size());
1692 | }
1693 |
1694 | static inline int display_width(const std::wstring &input) {
1695 | return details::mk_wcswidth(input.c_str(), input.size());
1696 | }
1697 |
1698 | } // namespace unicode
1699 |
1700 | #endif
1701 | // #include
1702 | // #include
1703 |
1704 | #include
1705 | #include
1706 | #include
1707 | #include
1708 | #include
1709 | #include
1710 |
1711 | #include
1712 | #include
1713 |
1714 | namespace indicators {
1715 | namespace details {
1716 |
1717 | inline void set_stream_color(std::ostream &os, Color color) {
1718 | switch (color) {
1719 | case Color::grey:
1720 | os << termcolor::grey;
1721 | break;
1722 | case Color::red:
1723 | os << termcolor::red;
1724 | break;
1725 | case Color::green:
1726 | os << termcolor::green;
1727 | break;
1728 | case Color::yellow:
1729 | os << termcolor::yellow;
1730 | break;
1731 | case Color::blue:
1732 | os << termcolor::blue;
1733 | break;
1734 | case Color::magenta:
1735 | os << termcolor::magenta;
1736 | break;
1737 | case Color::cyan:
1738 | os << termcolor::cyan;
1739 | break;
1740 | case Color::white:
1741 | os << termcolor::white;
1742 | break;
1743 | default:
1744 | assert(false);
1745 | }
1746 | }
1747 |
1748 | inline void set_font_style(std::ostream &os, FontStyle style) {
1749 | switch (style) {
1750 | case FontStyle::bold:
1751 | os << termcolor::bold;
1752 | break;
1753 | case FontStyle::dark:
1754 | os << termcolor::dark;
1755 | break;
1756 | case FontStyle::italic:
1757 | os << termcolor::italic;
1758 | break;
1759 | case FontStyle::underline:
1760 | os << termcolor::underline;
1761 | break;
1762 | case FontStyle::blink:
1763 | os << termcolor::blink;
1764 | break;
1765 | case FontStyle::reverse:
1766 | os << termcolor::reverse;
1767 | break;
1768 | case FontStyle::concealed:
1769 | os << termcolor::concealed;
1770 | break;
1771 | case FontStyle::crossed:
1772 | os << termcolor::crossed;
1773 | break;
1774 | default:
1775 | break;
1776 | }
1777 | }
1778 |
1779 | inline std::ostream &write_duration(std::ostream &os, std::chrono::nanoseconds ns) {
1780 | using namespace std;
1781 | using namespace std::chrono;
1782 | using days = duration>;
1783 | char fill = os.fill();
1784 | os.fill('0');
1785 | auto d = duration_cast(ns);
1786 | ns -= d;
1787 | auto h = duration_cast(ns);
1788 | ns -= h;
1789 | auto m = duration_cast(ns);
1790 | ns -= m;
1791 | auto s = duration_cast(ns);
1792 | if (d.count() > 0)
1793 | os << setw(2) << d.count() << "d:";
1794 | if (h.count() > 0)
1795 | os << setw(2) << h.count() << "h:";
1796 | os << setw(2) << m.count() << "m:" << setw(2) << s.count() << 's';
1797 | os.fill(fill);
1798 | return os;
1799 | }
1800 |
1801 | class BlockProgressScaleWriter {
1802 | public:
1803 | BlockProgressScaleWriter(std::ostream &os, size_t bar_width) : os(os), bar_width(bar_width) {}
1804 |
1805 | std::ostream &write(float progress) {
1806 | std::string fill_text{"█"};
1807 | std::vector lead_characters{" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"};
1808 | auto value = (std::min)(1.0f, (std::max)(0.0f, progress / 100.0f));
1809 | auto whole_width = std::floor(value * bar_width);
1810 | auto remainder_width = fmod((value * bar_width), 1.0f);
1811 | auto part_width = std::floor(remainder_width * lead_characters.size());
1812 | std::string lead_text = lead_characters[size_t(part_width)];
1813 | if ((bar_width - whole_width - 1) < 0)
1814 | lead_text = "";
1815 | for (size_t i = 0; i < whole_width; ++i)
1816 | os << fill_text;
1817 | os << lead_text;
1818 | for (size_t i = 0; i < (bar_width - whole_width - 1); ++i)
1819 | os << " ";
1820 | return os;
1821 | }
1822 |
1823 | private:
1824 | std::ostream &os;
1825 | size_t bar_width = 0;
1826 | };
1827 |
1828 | class ProgressScaleWriter {
1829 | public:
1830 | ProgressScaleWriter(std::ostream &os, size_t bar_width, const std::string &fill,
1831 | const std::string &lead, const std::string &remainder)
1832 | : os(os), bar_width(bar_width), fill(fill), lead(lead), remainder(remainder) {}
1833 |
1834 | std::ostream &write(float progress) {
1835 | auto pos = static_cast(progress * bar_width / 100.0);
1836 | for (size_t i = 0, current_display_width = 0; i < bar_width;) {
1837 | std::string next;
1838 |
1839 | if (i < pos) {
1840 | next = fill;
1841 | current_display_width = unicode::display_width(fill);
1842 | } else if (i == pos) {
1843 | next = lead;
1844 | current_display_width = unicode::display_width(lead);
1845 | } else {
1846 | next = remainder;
1847 | current_display_width = unicode::display_width(remainder);
1848 | }
1849 |
1850 | i += current_display_width;
1851 |
1852 | if (i > bar_width) {
1853 | // `next` is larger than the allowed bar width
1854 | // fill with empty space instead
1855 | os << std::string((bar_width - (i - current_display_width)), ' ');
1856 | break;
1857 | }
1858 |
1859 | os << next;
1860 | }
1861 | return os;
1862 | }
1863 |
1864 | private:
1865 | std::ostream &os;
1866 | size_t bar_width = 0;
1867 | std::string fill;
1868 | std::string lead;
1869 | std::string remainder;
1870 | };
1871 |
1872 | class IndeterminateProgressScaleWriter {
1873 | public:
1874 | IndeterminateProgressScaleWriter(std::ostream &os, size_t bar_width, const std::string &fill,
1875 | const std::string &lead)
1876 | : os(os), bar_width(bar_width), fill(fill), lead(lead) {}
1877 |
1878 | std::ostream &write(size_t progress) {
1879 | for (size_t i = 0; i < bar_width;) {
1880 | std::string next;
1881 | size_t current_display_width = 0;
1882 |
1883 | if (i < progress) {
1884 | next = fill;
1885 | current_display_width = unicode::display_width(fill);
1886 | } else if (i == progress) {
1887 | next = lead;
1888 | current_display_width = unicode::display_width(lead);
1889 | } else {
1890 | next = fill;
1891 | current_display_width = unicode::display_width(fill);
1892 | }
1893 |
1894 | i += current_display_width;
1895 |
1896 | if (i > bar_width) {
1897 | // `next` is larger than the allowed bar width
1898 | // fill with empty space instead
1899 | os << std::string((bar_width - (i - current_display_width)), ' ');
1900 | break;
1901 | }
1902 |
1903 | os << next;
1904 | }
1905 | return os;
1906 | }
1907 |
1908 | private:
1909 | std::ostream &os;
1910 | size_t bar_width = 0;
1911 | std::string fill;
1912 | std::string lead;
1913 | };
1914 |
1915 | } // namespace details
1916 | } // namespace indicators
1917 |
1918 | #endif
1919 |
1920 |
1921 | #ifndef INDICATORS_PROGRESS_BAR
1922 | #define INDICATORS_PROGRESS_BAR
1923 |
1924 | // #include
1925 |
1926 | #include
1927 | #include
1928 | #include
1929 | #include
1930 | // #include
1931 | // #include
1932 | // #include
1933 | #include
1934 | #include
1935 | #include
1936 | #include
1937 | #include
1938 | #include
1939 | #include
1940 | #include
1941 | #include
1942 |
1943 | namespace indicators {
1944 |
1945 | class ProgressBar {
1946 | using Settings =
1947 | std::tuple;
1955 |
1956 | public:
1957 | template ::type...>::value,
1961 | void *>::type = nullptr>
1962 | explicit ProgressBar(Args &&... args)
1963 | : settings_(
1964 | details::get(
1965 | option::BarWidth{100}, std::forward(args)...),
1966 | details::get(
1967 | option::PrefixText{}, std::forward(args)...),
1968 | details::get(
1969 | option::PostfixText{}, std::forward(args)...),
1970 | details::get(
1971 | option::Start{"["}, std::forward(args)...),
1972 | details::get(
1973 | option::End{"]"}, std::forward(args)...),
1974 | details::get(
1975 | option::Fill{"="}, std::forward(args)...),
1976 | details::get(
1977 | option::Lead{">"}, std::forward(args)...),
1978 | details::get(
1979 | option::Remainder{" "}, std::forward(args)...),
1980 | details::get(
1981 | option::MaxPostfixTextLen{0}, std::forward(args)...),
1982 | details::get(
1983 | option::Completed{false}, std::forward(args)...),
1984 | details::get(
1985 | option::ShowPercentage{false}, std::forward(args)...),
1986 | details::get(
1987 | option::ShowElapsedTime{false}, std::forward(args)...),
1988 | details::get(
1989 | option::ShowRemainingTime{false}, std::forward(args)...),
1990 | details::get(
1991 | option::SavedStartTime{false}, std::forward(args)...),
1992 | details::get(
1993 | option::ForegroundColor{Color::unspecified},
1994 | std::forward(args)...),
1995 | details::get(
1996 | option::FontStyles{std::vector{}},
1997 | std::forward(args)...),
1998 | details::get(
1999 | option::MinProgress{0}, std::forward(args)...),
2000 | details::get(
2001 | option::MaxProgress{100}, std::forward(args)...),
2002 | details::get(
2003 | option::ProgressType{ProgressType::incremental},
2004 | std::forward(args)...),
2005 | details::get(
2006 | option::Stream{std::cout}, std::forward(args)...)) {
2007 |
2008 | // if progress is incremental, start from min_progress
2009 | // else start from max_progress
2010 | const auto type = get_value();
2011 | if (type == ProgressType::incremental)
2012 | progress_ = get_value