0.3.0
Loading...
Searching...
No Matches
Colormap.cpp
Go to the documentation of this file.
1// This file is part of INSTINCT, the INS Toolkit for Integrated
2// Navigation Concepts and Training by the Institute of Navigation of
3// the University of Stuttgart, Germany.
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9/// @file Colormap.cpp
10/// @brief Colormap
11/// @author T. Topp (topp@ins.uni-stuttgart.de)
12/// @date 2023-09-22
13
14#include "Colormap.hpp"
15
16#include <algorithm>
17#include <chrono>
18
20#include "util/Logger.hpp"
21#include "util/ImGui.hpp"
22
23namespace NAV
24{
25
26std::vector<Colormap> ColormapsGlobal;
27std::vector<Colormap> ColormapsFlow;
28
30{
31 id = std::chrono::duration_cast<std::chrono::microseconds>(
32 std::chrono::system_clock::now().time_since_epoch())
33 .count();
34}
35
36void Colormap::addColor(double value, ImColor color)
37{
38 colormap.insert(std::upper_bound(colormap.begin(), colormap.end(), value, // NOLINT(boost-use-ranges,modernize-use-ranges) // ranges::upper_bound with lambda is not supported yet
39 [](double value, const std::pair<double, ImColor>& item) { return value < item.first; }),
40 std::make_pair(value, color));
41 version++;
42}
43
44void Colormap::removeColor(size_t idx)
45{
46 if (idx >= colormap.size() || colormap.size() == 1) { return; }
47 colormap.erase(colormap.begin() + static_cast<std::ptrdiff_t>(idx));
48 version++;
49}
50
51ImColor Colormap::getColor(double value, const ImColor& defaultColor) const
52{
53 for (size_t i = colormap.size() - 1;; i--)
54 {
55 const auto& item = colormap.at(i);
56 if (value >= item.first)
57 {
58 if (discrete || i == colormap.size() - 1) { return item.second; }
59
60 const auto& other = colormap.at(i + 1);
61
62 double t = (value - item.first) / (other.first - item.first);
63
64 return item.second.Value + static_cast<float>(t) * (other.second.Value - item.second.Value);
65 }
66
67 if (i == 0) { break; }
68 }
69
70 return defaultColor;
71}
72
73int64_t Colormap::getId() const
74{
75 return id;
76}
77
78const std::vector<std::pair<double, ImColor>>& Colormap::getColormap() const
79{
80 return colormap;
81}
82
83void Colormap::render() const
84{
85 const ImVec2 pos = ImGui::GetCursorScreenPos();
86 const float w = ImGui::CalcItemWidth();
87 const float h = ImGui::GetFrameHeight();
88 const ImRect bounds = ImRect(pos.x, pos.y, pos.x + w, pos.y + h);
89
90 render(bounds);
91}
92
93void Colormap::render(const ImRect& bounds) const
94{
95 if (auto* drawList = ImGui::GetWindowDrawList())
96 {
97 if (colormap.empty())
98 {
99 drawList->AddRectFilled(bounds.Min, bounds.Max, IM_COL32(25, 25, 25, 255));
100 return;
101 }
102
103 const size_t n = colormap.size() >= 2 ? (discrete ? colormap.size() : colormap.size() - 1) : colormap.size();
104
105 const float step = bounds.GetWidth() / static_cast<float>(n);
106 ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Min.x + step, bounds.Max.y);
107 for (size_t i = 0; i < n; ++i)
108 {
109 ImU32 col1 = colormap.at(i).second;
110 ImU32 col2 = discrete || colormap.size() == 1 ? col1 : ImU32(colormap.at(i + 1).second);
111
112 drawList->AddRectFilledMultiColor(rect.Min, rect.Max, col1, col2, col2, col1);
113 rect.TranslateX(step);
114 }
115 }
116}
117
118bool ColormapButton(const char* label, Colormap& cmap, const ImVec2& size_arg)
119{
120 ImGuiContext& G = *GImGui;
121 const ImGuiStyle& style = G.Style;
122 ImGuiWindow* Window = G.CurrentWindow;
123 if (Window->SkipItems) { return false; }
124
125 const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos;
126 const ImVec2 label_size = ImGui::CalcTextSize(label, nullptr, true);
127 ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0F, label_size.y + style.FramePadding.y * 2.0F);
128 const ImRect rect = ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y);
129 cmap.render(rect);
130 ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32_BLACK_TRANS);
131 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1, 1, 1, 0.1F));
132 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1, 1, 1, 0.2F));
133 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0);
134 const bool pressed = ImGui::Button(label, size);
135 ImGui::PopStyleColor(3);
136 ImGui::PopStyleVar(1);
137
138 bool changed = false;
139
140 if (pressed) { ImGui::OpenPopup(fmt::format("Colormap##{}", label).c_str()); }
141 if (ImGui::BeginPopup(fmt::format("Colormap##{}", label).c_str()))
142 {
143 if (ImGui::BeginTable(fmt::format("##{} colormap table", label).c_str(), 5, ImGuiTableFlags_SizingFixedFit, ImVec2(0.0F, 0.0F)))
144 {
145 ImGui::TableSetupColumn("Value");
146 ImGui::TableSetupColumn("Color");
147 ImGui::TableSetupColumn("");
148 ImGui::TableSetupColumn("");
149 ImGui::TableSetupColumn("");
150
151 int colormapRemovalIdx = -1;
152 int insertIdx = -1;
153 ImGui::TableHeadersRow();
154 for (size_t i = 0; i < cmap.colormap.size(); i++)
155 {
156 auto& [value, color] = cmap.colormap.at(i);
157 ImGui::TableNextColumn();
158 ImGui::SetNextItemWidth(100.0F);
159 if (ImGui::InputDoubleL(fmt::format("##{} colormap value {}", label, i).c_str(), &value,
160 i != 0 ? cmap.colormap.at(i - 1).first : std::numeric_limits<double>::lowest(),
161 i != cmap.colormap.size() - 1 ? cmap.colormap.at(i + 1).first : std::numeric_limits<double>::max(),
162 0.0, 0.0, "%.6g", ImGuiInputTextFlags_CharsScientific))
163 {
164 cmap.version++;
165 changed = true;
166 }
167
168 ImGui::TableNextColumn();
169 ImGui::SetNextItemWidth(300.0F);
170 if (ImGui::ColorEdit4(fmt::format("##{} colormap color {}", label, i).c_str(), &color.Value.x))
171 {
172 cmap.version++;
173 changed = true;
174 }
175
176 ImGui::TableNextColumn();
177 if (ImGui::Button(fmt::format("⌃##{} insert colormap above {}", label, i).c_str())) { insertIdx = static_cast<int>(i); }
178 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Insert entry above?"); }
179
180 ImGui::TableNextColumn();
181 if (ImGui::Button(fmt::format("⌄##{} insert colormap below {}", label, i).c_str())) { insertIdx = static_cast<int>(i) + 1; }
182 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Insert entry below?"); }
183
184 ImGui::TableNextColumn();
185 if (cmap.colormap.size() > 1)
186 {
187 if (ImGui::Button(fmt::format("X##{} remove colormap {}", label, i).c_str())) { colormapRemovalIdx = static_cast<int>(i); }
188 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Remove?"); }
189 }
190 }
191 if (colormapRemovalIdx >= 0)
192 {
193 cmap.colormap.erase(cmap.colormap.begin() + static_cast<std::ptrdiff_t>(colormapRemovalIdx));
194 cmap.version++;
195 changed = true;
196 }
197 if (insertIdx >= 0)
198 {
199 cmap.colormap.insert(cmap.colormap.begin() + static_cast<std::ptrdiff_t>(insertIdx),
200 std::make_pair(cmap.colormap.at(static_cast<size_t>(insertIdx != static_cast<int>(cmap.colormap.size()) ? insertIdx : insertIdx - 1)).first,
201 ImColor(1.0F, 1.0F, 1.0F, 1.0F)));
202 cmap.version++;
203 changed = true;
204 }
205
206 ImGui::EndTable();
207 }
208
209 ImGui::EndPopup();
210 }
211
212 return changed;
213}
214
215void to_json(json& j, const Colormap& cmap)
216{
217 j = json{
218 { "name", cmap.name },
219 { "discrete", cmap.discrete },
220 { "id", cmap.id },
221 { "colormap", cmap.colormap },
222 };
223}
224
225void from_json(const json& j, Colormap& cmap)
226{
227 if (j.contains("name"))
228 {
229 j.at("name").get_to(cmap.name);
230 }
231 if (j.contains("discrete"))
232 {
233 j.at("discrete").get_to(cmap.discrete);
234 }
235 if (j.contains("id"))
236 {
237 j.at("id").get_to(cmap.id);
238 }
239 if (j.contains("colormap"))
240 {
241 j.at("colormap").get_to(cmap.colormap);
242 }
243}
244
245bool ShowColormapSelector(ColormapMaskType& type, int64_t& id, const char* label)
246{
247 bool changes = false;
248
249 auto activeColormap = ColormapSearch(type, id);
250
251 std::string colormapName = activeColormap ? activeColormap->get().name : "";
252 if (ImGui::BeginCombo(fmt::format("{}Colormap Mask", label).c_str(), colormapName.c_str()))
253 {
254 if (ImGui::Selectable("##Empty colormap mask", colormapName.empty(), 0))
255 {
256 if (id != -1 || type != ColormapMaskType::None)
257 {
258 id = -1;
260 changes = true;
261 }
262 }
263 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
264 if (colormapName.empty()) { ImGui::SetItemDefaultFocus(); }
265
266 for (const auto& cmap : ColormapsGlobal)
267 {
268 const bool is_selected = (cmap.getId() == id);
269 if (ImGui::Selectable(fmt::format("G: {}##Global colormap", cmap.name).c_str(), is_selected, 0))
270 {
271 if (id != cmap.getId() || type != ColormapMaskType::Global)
272 {
273 id = cmap.getId();
275 changes = true;
276 }
277 }
278 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
279 if (is_selected) { ImGui::SetItemDefaultFocus(); }
280 }
281 for (const auto& cmap : ColormapsFlow)
282 {
283 const bool is_selected = (cmap.getId() == id);
284 if (ImGui::Selectable(fmt::format("F: {}##Flow colormap", cmap.name).c_str(), is_selected, 0))
285 {
286 if (id != cmap.getId() || type != ColormapMaskType::Flow)
287 {
288 id = cmap.getId();
290 changes = true;
291 }
292 }
293 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
294 if (is_selected) { ImGui::SetItemDefaultFocus(); }
295 }
296 ImGui::EndCombo();
297 }
298 return changes;
299}
300
301std::optional<std::reference_wrapper<const Colormap>> ColormapSearch(const ColormapMaskType& type, const int64_t& id)
302{
303 switch (type)
304 {
306 break;
308 if (auto iter = std::ranges::find_if(ColormapsGlobal, [&id](const Colormap& cmap) { return id == cmap.getId(); });
309 iter != ColormapsGlobal.end())
310 {
311 return std::cref(*iter);
312 }
313 break;
315 if (auto iter = std::ranges::find_if(ColormapsFlow, [&id](const Colormap& cmap) { return id == cmap.getId(); });
316 iter != ColormapsFlow.end())
317 {
318 return std::cref(*iter);
319 }
320 break;
321 }
322 return {};
323}
324
325} // namespace NAV
Colormap.
nlohmann::json json
json namespace
ImGui Helper.
Utility class for logging to console and file.
Colormap class.
Definition Colormap.hpp:48
const std::vector< std::pair< double, ImColor > > & getColormap() const
Return the map.
Definition Colormap.cpp:78
ImColor getColor(double value, const ImColor &defaultColor) const
Gets the color for the given value.
Definition Colormap.cpp:51
void removeColor(size_t idx)
Remove the entry at the index (if past the last index or empty, NoOp)
Definition Colormap.cpp:44
int64_t id
Unique id of the colormap.
Definition Colormap.hpp:80
bool discrete
Whether to have discrete changes of the colors or continuous.
Definition Colormap.hpp:75
std::string name
Name of the Colormap.
Definition Colormap.hpp:74
int64_t getId() const
Return the id of the colormap.
Definition Colormap.cpp:73
std::vector< std::pair< double, ImColor > > colormap
Sorted list of value/color combinations (value is active if lookup is greater or equal)
Definition Colormap.hpp:82
size_t version
Version, to tell nodes that the colormap was updated.
Definition Colormap.hpp:76
void render() const
Renders the colormap.
Definition Colormap.cpp:83
Colormap()
Constructor.
Definition Colormap.cpp:29
void addColor(double value, ImColor color)
Add a color with value.
Definition Colormap.cpp:36
ImGui extensions.
bool InputDoubleL(const char *label, double *v, double v_min, double v_max, double step, double step_fast, const char *format, ImGuiInputTextFlags flags)
Shows a value limited InputText GUI element for 'double'.
Definition imgui_ex.cpp:294
void to_json(json &j, const Node &node)
Converts the provided node into a json object.
Definition Node.cpp:990
ColormapMaskType
Type of the Colormap mask.
Definition Colormap.hpp:118
@ Global
Use the global colormap.
Definition Colormap.hpp:120
@ None
Do not use a colormap mask.
Definition Colormap.hpp:119
@ Flow
Use the flow colormap.
Definition Colormap.hpp:121
std::vector< Colormap > ColormapsGlobal
Global colormaps.
Definition Colormap.cpp:26
bool ShowColormapSelector(ColormapMaskType &type, int64_t &id, const char *label)
Shows a combobox to select a colormap.
Definition Colormap.cpp:245
void from_json(const json &j, Node &node)
Converts the provided json object into a node object.
Definition Node.cpp:1007
std::vector< Colormap > ColormapsFlow
Flow colormaps.
Definition Colormap.cpp:27
bool ColormapButton(const char *label, Colormap &cmap, const ImVec2 &size_arg)
Display a colormap button.
Definition Colormap.cpp:118
std::optional< std::reference_wrapper< const Colormap > > ColormapSearch(const ColormapMaskType &type, const int64_t &id)
Searches for the colormap in the Global and Flow colormaps.
Definition Colormap.cpp:301