INSTINCT Code Coverage Report


Directory: src/
File: util/Plot/PlotItemStyle.cpp
Date: 2025-11-25 23:34:18
Exec Total Coverage
Lines: 29 370 7.8%
Functions: 1 5 20.0%
Branches: 31 716 4.3%

Line Branch Exec Source
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 #include "PlotItemStyle.hpp"
10
11 #include <cstddef>
12 #include <functional>
13 #include <imgui.h>
14 #include <imgui_stdlib.h>
15 #include <implot.h>
16 #include <implot_internal.h>
17 #include <muParser.h>
18 #include <string>
19
20 #include "internal/gui/widgets/HelpMarker.hpp"
21 #include "util/Logger.hpp"
22
23 namespace NAV
24 {
25
26 /// @brief Write info to a json object
27 /// @param[out] j Json output
28 /// @param[in] style Object to read info from
29 ✗ void to_json(json& j, const PlotItemStyle& style)
30 {
31 ✗ j = json{
32 ✗ { "legendName", style.legendName },
33 ✗ { "stride", style.stride },
34 ✗ { "lineType", style.lineType },
35 ✗ { "color", style.color },
36 ✗ { "colormapMask", style.colormapMask },
37 ✗ { "colormapMaskDataCmpIdx", style.colormapMaskDataCmpIdx },
38 ✗ { "thickness", style.thickness },
39 ✗ { "markerColormapMask", style.markerColormapMask },
40 ✗ { "markerColormapMaskDataCmpIdx", style.markerColormapMaskDataCmpIdx },
41 ✗ { "markers", style.markers },
42 ✗ { "markerStyle", style.markerStyle },
43 ✗ { "markerSize", style.markerSize },
44 ✗ { "markerWeight", style.markerWeight },
45 ✗ { "markerFillColor", style.markerFillColor },
46 ✗ { "markerOutlineColor", style.markerOutlineColor },
47 ✗ { "errorBoundsEnabled", style.errorBoundsEnabled },
48 ✗ { "errorBoundsDataIdx", style.errorBoundsDataIdx },
49 ✗ { "errorBoundsAlpha", style.errorBoundsAlpha },
50 ✗ { "errorBoundsModifierExpression", style.errorBoundsModifierExpression },
51 ✗ { "eventsEnabled", style.eventsEnabled },
52 ✗ { "eventMarkerStyle", style.eventMarkerStyle },
53 ✗ { "eventMarkerSize", style.eventMarkerSize },
54 ✗ { "eventMarkerWeight", style.eventMarkerWeight },
55 ✗ { "eventMarkerFillColor", style.eventMarkerFillColor },
56 ✗ { "eventMarkerOutlineColor", style.eventMarkerOutlineColor },
57 ✗ { "eventTooltipFilterRegex", style.eventTooltipFilterRegex },
58 ✗ };
59 ✗ if (style.lineFlags) { j["lineFlags"] = style.lineFlags.value(); }
60 ✗ }
61 /// @brief Read info from a json object
62 /// @param[in] j Json variable to read info from
63 /// @param[out] style Output object
64 325 void from_json(const json& j, PlotItemStyle& style)
65 {
66
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("legendName")) { j.at("legendName").get_to(style.legendName); }
67
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("stride")) { j.at("stride").get_to(style.stride); }
68
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("lineType")) { j.at("lineType").get_to(style.lineType); }
69
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("color")) { j.at("color").get_to(style.color); }
70
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("colormapMask")) { j.at("colormapMask").get_to(style.colormapMask); }
71
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("colormapMaskDataCmpIdx")) { j.at("colormapMaskDataCmpIdx").get_to(style.colormapMaskDataCmpIdx); }
72
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("thickness")) { j.at("thickness").get_to(style.thickness); }
73
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 325 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
325 if (j.contains("lineFlags")) { style.lineFlags.emplace(j.at("lineFlags").get<uint32_t>()); }
74
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("markerColormapMask")) { j.at("markerColormapMask").get_to(style.markerColormapMask); }
75
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("markerColormapMaskDataCmpIdx")) { j.at("markerColormapMaskDataCmpIdx").get_to(style.markerColormapMaskDataCmpIdx); }
76
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("markers")) { j.at("markers").get_to(style.markers); }
77
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("markerStyle")) { j.at("markerStyle").get_to(style.markerStyle); }
78
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("markerSize")) { j.at("markerSize").get_to(style.markerSize); }
79
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("markerWeight")) { j.at("markerWeight").get_to(style.markerWeight); }
80
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("markerFillColor")) { j.at("markerFillColor").get_to(style.markerFillColor); }
81
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("markerOutlineColor")) { j.at("markerOutlineColor").get_to(style.markerOutlineColor); }
82
2/2
✓ Branch 1 taken 224 times.
✓ Branch 2 taken 101 times.
325 if (j.contains("errorBoundsEnabled")) { j.at("errorBoundsEnabled").get_to(style.errorBoundsEnabled); }
83
2/2
✓ Branch 1 taken 224 times.
✓ Branch 2 taken 101 times.
325 if (j.contains("errorBoundsDataIdx")) { j.at("errorBoundsDataIdx").get_to(style.errorBoundsDataIdx); }
84
2/2
✓ Branch 1 taken 224 times.
✓ Branch 2 taken 101 times.
325 if (j.contains("errorBoundsAlpha")) { j.at("errorBoundsAlpha").get_to(style.errorBoundsAlpha); }
85
2/2
✓ Branch 1 taken 224 times.
✓ Branch 2 taken 101 times.
325 if (j.contains("errorBoundsModifierExpression")) { j.at("errorBoundsModifierExpression").get_to(style.errorBoundsModifierExpression); }
86
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("eventsEnabled")) { j.at("eventsEnabled").get_to(style.eventsEnabled); }
87
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("eventMarkerStyle")) { j.at("eventMarkerStyle").get_to(style.eventMarkerStyle); }
88
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("eventMarkerSize")) { j.at("eventMarkerSize").get_to(style.eventMarkerSize); }
89
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("eventMarkerWeight")) { j.at("eventMarkerWeight").get_to(style.eventMarkerWeight); }
90
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("eventMarkerFillColor")) { j.at("eventMarkerFillColor").get_to(style.eventMarkerFillColor); }
91
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("eventMarkerOutlineColor")) { j.at("eventMarkerOutlineColor").get_to(style.eventMarkerOutlineColor); }
92
1/2
✓ Branch 1 taken 325 times.
✗ Branch 2 not taken.
325 if (j.contains("eventTooltipFilterRegex")) { j.at("eventTooltipFilterRegex").get_to(style.eventTooltipFilterRegex); }
93 325 }
94
95 ✗ PlotItemStyle::LegendPopupReturn PlotItemStyle::showLegendPopup(const char* id,
96 const char* displayTitle,
97 int plotDataBufferSize,
98 int plotElementIdx,
99 [[maybe_unused]] const char* nameId,
100 ImPlotLineFlags plotLineFlags,
101 ScrollingBuffer<ImU32>* colormapMaskColors,
102 ScrollingBuffer<ImU32>* markerColormapMaskColors,
103 const std::function<bool(size_t&, const char*)>& ShowDataReferenceChooser,
104 ScrollingBuffer<double>* eventMarker,
105 std::vector<std::tuple<double, double, PlotEventTooltip>>* eventTooltips)
106 {
107 ✗ LegendPopupReturn ret;
108
109 // Legend item context menu (right click on legend item)
110 ✗ if (ImPlot::BeginLegendPopup(id))
111 {
112 ✗ if (displayTitle)
113 {
114 ✗ ImGui::TextUnformatted(displayTitle);
115 ✗ ImGui::Separator();
116 }
117
118 ✗ if (legendNameGui.empty())
119 {
120 ✗ legendNameGui = legendName;
121 }
122 ✗ ImGui::InputText("Legend name", &legendNameGui);
123 ✗ if (legendNameGui != legendName && !ImGui::IsItemActive())
124 {
125 ✗ legendName = legendNameGui;
126 ✗ ret.changed = true;
127 ✗ LOG_DEBUG("{}: Legend changed to {}", nameId, legendName);
128 }
129
130 ✗ if (ImGui::InputInt("Stride", &stride))
131 {
132 ✗ stride = std::max(stride, 0);
133 ✗ stride = std::min(stride, plotDataBufferSize - 1);
134 ✗ ret.changed = true;
135 ✗ LOG_DEBUG("{}: Stride changed to {}", nameId, stride);
136 }
137
138 ✗ if (auto lt = static_cast<int>(lineType);
139 ✗ ImGui::Combo("Style", &lt, "Scatter\0Line\0\0"))
140 {
141 ✗ lineType = static_cast<decltype(lineType)>(lt);
142 ✗ ret.changed = true;
143 }
144 ✗ ImPlotLineFlags lFlags = lineFlags.value_or(plotLineFlags);
145 ✗ bool plotItemLineFlagsAuto = !lineFlags.has_value();
146 ✗ if (plotItemLineFlagsAuto) { ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.8F); }
147 ✗ if (ImGui::CheckboxFlags("NoClip", &lFlags, ImPlotLineFlags_NoClip))
148 {
149 ✗ lineFlags = lFlags;
150 ✗ ret.changed = true;
151 }
152 ✗ if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Markers (if displayed) on the edge of a plot will not be clipped"); }
153 ✗ ImGui::SameLine();
154 ✗ if (ImGui::CheckboxFlags("SkipNaN", &lFlags, ImPlotLineFlags_SkipNaN))
155 {
156 ✗ lineFlags = lFlags;
157 ✗ ret.changed = true;
158 }
159 ✗ if (ImGui::IsItemHovered()) { ImGui::SetTooltip("NaNs values will be skipped instead of rendered as missing data"); }
160 ✗ ImGui::SameLine();
161 ✗ if (ImGui::CheckboxFlags("Loop", &lFlags, ImPlotLineFlags_Loop))
162 {
163 ✗ lineFlags = lFlags;
164 ✗ ret.changed = true;
165 }
166 ✗ if (ImGui::IsItemHovered()) { ImGui::SetTooltip("The last and first point will be connected to form a closed loop"); }
167 ✗ if (lineFlags)
168 {
169 ✗ ImGui::SameLine();
170 ✗ if (ImGui::Button("Auto##Line Flags"))
171 {
172 ✗ lineFlags.reset();
173 }
174 }
175 ✗ if (plotItemLineFlagsAuto) { ImGui::PopStyleVar(); }
176 ✗ if (lineType == PlotItemStyle::LineType::Line)
177 {
178 ✗ if (ImGui::DragFloat("Line Thickness", &thickness, 0.1F, 0.0F, 8.0F, "%.2f px"))
179 {
180 ✗ ret.changed = true;
181 }
182 ✗ if (colormapMaskColors && ShowColormapSelector(colormapMask.first, colormapMask.second))
183 {
184 ✗ colormapMaskColors->clear();
185 ✗ ret.changed = true;
186 }
187 ✗ if (colormapMaskColors && colormapMask.first != ColormapMaskType::None
188 ✗ && ShowDataReferenceChooser(colormapMaskDataCmpIdx, "Colormap Ref"))
189 {
190 ✗ colormapMaskColors->clear();
191 ✗ ret.changed = true;
192 }
193 ✗ if (colormapMask.first == ColormapMaskType::None)
194 {
195 ✗ bool isColorAuto = ImPlot::IsColorAuto(color);
196 ✗ auto col = isColorAuto ? ImPlot::GetColormapColor(plotElementIdx) : color;
197 ✗ if (ImGui::ColorEdit4("Line Color", &col.x))
198 {
199 ✗ color = col;
200 ✗ ret.changed = true;
201 }
202 ✗ if (!isColorAuto)
203 {
204 ✗ ImGui::SameLine();
205 ✗ if (ImGui::Button("Auto##Line Color"))
206 {
207 ✗ color = IMPLOT_AUTO_COL;
208 }
209 }
210 }
211 ✗ if (ImGui::Checkbox("Markers", &markers))
212 {
213 ✗ ret.changed = true;
214 }
215 }
216 ✗ if (lineType == PlotItemStyle::LineType::Scatter || markers)
217 {
218 ✗ if (ImGui::Combo("Marker Style", &markerStyle,
219 "Circle\0Square\0Diamond\0Up\0Down\0Left\0Right\0Cross\0Plus\0Asterisk\0\0"))
220 {
221 ✗ ret.changed = true;
222 }
223 ✗ if (ImGui::DragFloat("Marker Size", &markerSize, 0.1F, 1.0F, 10.0F, "%.2f px"))
224 {
225 ✗ ret.changed = true;
226 }
227 ✗ if (ImGui::DragFloat("Marker Weight", &markerWeight, 0.05F, 0.5F, 3.0F, "%.2f px"))
228 {
229 ✗ ret.changed = true;
230 }
231 ✗ if (!markers)
232 {
233 ✗ if (colormapMaskColors && ShowColormapSelector(colormapMask.first, colormapMask.second))
234 {
235 ✗ colormapMaskColors->clear();
236 ✗ ret.changed = true;
237 }
238 ✗ if (colormapMaskColors && colormapMask.first != ColormapMaskType::None
239 ✗ && ShowDataReferenceChooser(colormapMaskDataCmpIdx, "Colormap Ref"))
240 {
241 ✗ colormapMaskColors->clear();
242 ✗ ret.changed = true;
243 }
244 }
245 ✗ if (markers && lineType != PlotItemStyle::LineType::Scatter)
246 {
247 ✗ if (colormapMaskColors && ShowColormapSelector(markerColormapMask.first, markerColormapMask.second, "Marker "))
248 {
249 ✗ markerColormapMaskColors->clear();
250 ✗ ret.changed = true;
251 }
252 ✗ if (colormapMaskColors && markerColormapMask.first != ColormapMaskType::None
253 ✗ && ShowDataReferenceChooser(markerColormapMaskDataCmpIdx, "Marker Colormap Ref"))
254 {
255 ✗ markerColormapMaskColors->clear();
256 ✗ ret.changed = true;
257 }
258 }
259 ✗ if (markerColormapMask.first == ColormapMaskType::None
260 ✗ && (lineType != PlotItemStyle::LineType::Scatter
261 ✗ || colormapMask.first == ColormapMaskType::None))
262 {
263 ✗ bool isColorAuto = ImPlot::IsColorAuto(markerFillColor);
264 ✗ auto col = isColorAuto ? ImPlot::GetColormapColor(plotElementIdx) : markerFillColor;
265 ✗ if (ImGui::ColorEdit4("Marker Fill Color", &col.x))
266 {
267 ✗ markerFillColor = col;
268 ✗ ret.changed = true;
269 }
270 ✗ if (!isColorAuto)
271 {
272 ✗ ImGui::SameLine();
273 ✗ if (ImGui::Button("Auto##Marker Fill Color"))
274 {
275 ✗ markerFillColor = IMPLOT_AUTO_COL;
276 }
277 }
278
279 ✗ isColorAuto = ImPlot::IsColorAuto(markerOutlineColor);
280 ✗ col = isColorAuto ? ImPlot::GetColormapColor(plotElementIdx) : markerOutlineColor;
281 ✗ if (ImGui::ColorEdit4("Marker Outline Color", &col.x))
282 {
283 ✗ markerOutlineColor = col;
284 ✗ ret.changed = true;
285 }
286 ✗ if (!isColorAuto)
287 {
288 ✗ ImGui::SameLine();
289 ✗ if (ImGui::Button("Auto##Marker Outline Color"))
290 {
291 ✗ markerOutlineColor = IMPLOT_AUTO_COL;
292 }
293 }
294 }
295 }
296 ✗ if (lineType == PlotItemStyle::LineType::Line)
297 {
298 ✗ ImGui::Separator();
299 ✗ ret.changed |= ImGui::Checkbox("Show Error Bounds", &errorBoundsEnabled);
300 ✗ if (errorBoundsEnabled)
301 {
302 ✗ auto idx = errorBoundsDataIdx;
303 ✗ if (ShowDataReferenceChooser(errorBoundsDataIdx, "Error Bounds Ref") && errorBoundsDataIdx != idx)
304 {
305 ✗ ret.changed = true;
306 ✗ ret.errorBoundsReCalcNeeded = true;
307 }
308 ✗ ret.changed |= ImGui::SliderFloat("Error Bounds Alpha", &errorBoundsAlpha, 0.0F, 1.0F, "%.2f");
309 ✗ if (errorBoundsModifierExpressionTemp.empty()) { errorBoundsModifierExpressionTemp = errorBoundsModifierExpression; }
310 ✗ std::string expression = errorBoundsModifierExpressionTemp;
311 ✗ if (errorBoundsModifierExpressionTemp != errorBoundsModifierExpression)
312 {
313 ✗ ImGui::PushStyleColor(ImGuiCol_Text, ImColor(220, 20, 60).Value);
314 }
315 ✗ ImGui::InputTextWithHint("Error Bounds Modifier", "e.g. '3 * x' or 'sqrt(x)'", &expression);
316 ✗ if (errorBoundsModifierExpressionTemp != errorBoundsModifierExpression)
317 {
318 ✗ ImGui::PopStyleColor();
319 ✗ if (!errorBoundsModifierExpression.empty() && ImGui::IsItemHovered()) { ImGui::SetTooltip("Currently used expression:\n%s", errorBoundsModifierExpression.c_str()); }
320 }
321 ✗ if (expression != errorBoundsModifierExpressionTemp && !ImGui::IsItemActive())
322 {
323 ✗ errorBoundsModifierExpressionTemp = expression;
324 try
325 {
326 ✗ if (!errorBoundsModifierExpressionTemp.empty())
327 {
328 ✗ double x = 1.0;
329 ✗ mu::Parser p;
330 ✗ p.DefineVar("x", &x);
331 ✗ p.SetExpr(errorBoundsModifierExpressionTemp);
332 ✗ x = p.Eval();
333 ✗ }
334
335 ✗ errorBoundsModifierExpression = errorBoundsModifierExpressionTemp;
336 ✗ errorBoundsModifierExpressionTemp.clear();
337 ✗ ret.changed = true;
338 ✗ ret.errorBoundsReCalcNeeded = true;
339 }
340 ✗ catch (mu::Parser::exception_type& e)
341 {
342 ✗ LOG_ERROR("{}: Error bound modifier parse error on '{}': {} in expression: {}", nameId, legendName, e.GetMsg(), errorBoundsModifierExpressionTemp);
343 ✗ }
344 }
345 ✗ ImGui::SameLine();
346 ✗ if (gui::widgets::BeginHelpMarker("(?)", 0.0F))
347 {
348 ✗ auto tableEntry = [](const char* first, const char* second, const char* third) {
349 ✗ ImGui::TableNextRow();
350 ✗ ImGui::TableNextColumn();
351 ✗ ImGui::TextUnformatted(first);
352 ✗ ImGui::TableNextColumn();
353 ✗ ImGui::TextUnformatted(second);
354 ✗ ImGui::TableNextColumn();
355 ✗ ImGui::TextUnformatted(third);
356 ✗ };
357
358 ✗ ImGui::BeginGroup();
359 {
360 ✗ ImGui::TextUnformatted("Functions");
361 ✗ if (ImGui::BeginTable(fmt::format("Functions##{}", id).c_str(), 3,
362 ✗ ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX, ImVec2(0.0F, 0.0F)))
363 {
364 ✗ ImGui::TableSetupColumn("Name");
365 ✗ ImGui::TableSetupColumn("Argc.");
366 ✗ ImGui::TableSetupColumn("Explanation");
367 ✗ ImGui::TableHeadersRow();
368
369 ✗ tableEntry("sin", "1", "sine function");
370 ✗ tableEntry("cos", "1", "cosine function");
371 ✗ tableEntry("tan", "1", "tangens function");
372 ✗ tableEntry("asin", "1", "arcus sine function");
373 ✗ tableEntry("acos", "1", "arcus cosine function");
374 ✗ tableEntry("atan", "1", "arcus tangens function");
375 ✗ tableEntry("sinh", "1", "hyperbolic sine function");
376 ✗ tableEntry("cosh", "1", "hyperbolic cosine");
377 ✗ tableEntry("tanh", "1", "hyperbolic tangens function");
378 ✗ tableEntry("asinh", "1", "hyperbolic arcus sine function");
379 ✗ tableEntry("acosh", "1", "hyperbolic arcus tangens function");
380 ✗ tableEntry("atanh", "1", "hyperbolic arcur tangens function");
381 ✗ tableEntry("log2", "1", "logarithm to the base 2");
382 ✗ tableEntry("log10", "1", "logarithm to the base 10");
383 ✗ tableEntry("log", "1", "logarithm to base e (2.71828...)");
384 ✗ tableEntry("ln", "1", "logarithm to base e (2.71828...)");
385 ✗ tableEntry("exp", "1", "e raised to the power of x");
386 ✗ tableEntry("sqrt", "1", "square root of a value");
387 ✗ tableEntry("sign", "1", "sign function -1 if x<0; 1 if x>0");
388 ✗ tableEntry("rint", "1", "round to nearest integer");
389 ✗ tableEntry("abs", "1", "absolute value");
390 ✗ tableEntry("min", "var.", "min of all arguments");
391 ✗ tableEntry("max", "var.", "max of all arguments");
392 ✗ tableEntry("sum", "var.", "sum of all arguments");
393 ✗ tableEntry("avg", "var.", "mean value of all arguments");
394
395 ✗ ImGui::EndTable();
396 }
397 }
398 ✗ ImGui::EndGroup();
399
400 ✗ ImGui::SameLine();
401
402 ✗ ImGui::BeginGroup();
403 {
404 ✗ ImGui::TextUnformatted("Binary operators");
405 ✗ if (ImGui::BeginTable(fmt::format("Binary operators##{}", id).c_str(), 3,
406 ✗ ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX, ImVec2(0.0F, 0.0F)))
407 {
408 ✗ ImGui::TableSetupColumn("Operator");
409 ✗ ImGui::TableSetupColumn("Description");
410 ✗ ImGui::TableSetupColumn("Priority");
411 ✗ ImGui::TableHeadersRow();
412
413 ✗ tableEntry("=", "assignement *", "0");
414 ✗ tableEntry("||", "logical or", "1");
415 ✗ tableEntry("&&", "logical and", "2");
416 ✗ tableEntry("|", "bitwise or", "3");
417 ✗ tableEntry("&", "bitwise and", "4");
418 ✗ tableEntry("<=", "less or equal", "5");
419 ✗ tableEntry(">=", "greater or equal", "5");
420 ✗ tableEntry("!=", "not equal", "5");
421 ✗ tableEntry("==", "equal", "5");
422 ✗ tableEntry(">", "greater than", "5");
423 ✗ tableEntry("<", "less than", "5");
424 ✗ tableEntry("+", "addition", "6");
425 ✗ tableEntry("-", "subtraction", "6");
426 ✗ tableEntry("*", "multiplication", "7");
427 ✗ tableEntry("/", "division", "7");
428 ✗ tableEntry("^", "raise x to the power of y", "8");
429
430 ✗ ImGui::EndTable();
431 }
432
433 ✗ ImGui::TextUnformatted("Ternary Operators");
434 ✗ if (ImGui::BeginTable(fmt::format("Ternary Operators##{}", id).c_str(), 3,
435 ✗ ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX, ImVec2(0.0F, 0.0F)))
436 {
437 ✗ ImGui::TableSetupColumn("Operator");
438 ✗ ImGui::TableSetupColumn("Description");
439 ✗ ImGui::TableSetupColumn("Remarks");
440 ✗ ImGui::TableHeadersRow();
441
442 ✗ tableEntry("?:", "if then else operator", "C++ style syntax");
443
444 ✗ ImGui::EndTable();
445 }
446
447 ✗ ImGui::TextUnformatted("Constants");
448 ✗ if (ImGui::BeginTable(fmt::format("Constant##{}", id).c_str(), 3,
449 ✗ ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX, ImVec2(0.0F, 0.0F)))
450 {
451 ✗ ImGui::TableSetupColumn("Constant");
452 ✗ ImGui::TableSetupColumn("Description");
453 ✗ ImGui::TableSetupColumn("Remarks");
454 ✗ ImGui::TableHeadersRow();
455
456 ✗ tableEntry("_pi", "The one and only pi", "3.14159265359");
457 ✗ tableEntry("_e", "Euler's number", "2.71828182846");
458
459 ✗ ImGui::EndTable();
460 }
461 }
462 ✗ ImGui::EndGroup();
463
464 ✗ gui::widgets::EndHelpMarker(false);
465 }
466 ✗ }
467 }
468 ✗ if (eventMarker && eventTooltips)
469 {
470 ✗ ImGui::Separator();
471 ✗ if (ImGui::Checkbox("Events", &eventsEnabled))
472 {
473 ✗ ret.changed = true;
474 }
475 ✗ if (eventsEnabled)
476 {
477 ✗ if (ImGui::Combo("Event Marker Style", &eventMarkerStyle,
478 "Circle\0Square\0Diamond\0Up\0Down\0Left\0Right\0Cross\0Plus\0Asterisk\0\0"))
479 {
480 ✗ ret.changed = true;
481 }
482 ✗ if (ImGui::DragFloat("Event Marker Size", &eventMarkerSize, 0.1F, 1.0F, 10.0F, "%.2f px"))
483 {
484 ✗ ret.changed = true;
485 }
486 ✗ if (ImGui::DragFloat("Event Marker Weight", &eventMarkerWeight, 0.05F, 0.5F, 3.0F, "%.2f px"))
487 {
488 ✗ ret.changed = true;
489 }
490 ✗ bool isColorAuto = ImPlot::IsColorAuto(eventMarkerFillColor);
491 ✗ auto col = isColorAuto ? ImPlot::GetColormapColor(plotElementIdx) : eventMarkerFillColor;
492 ✗ if (ImGui::ColorEdit4("Event Marker Fill Color", &col.x))
493 {
494 ✗ eventMarkerFillColor = col;
495 ✗ ret.changed = true;
496 }
497 ✗ if (!isColorAuto)
498 {
499 ✗ ImGui::SameLine();
500 ✗ if (ImGui::Button("Auto##Event Marker Fill Color"))
501 {
502 ✗ eventMarkerFillColor = IMPLOT_AUTO_COL;
503 }
504 }
505
506 ✗ isColorAuto = ImPlot::IsColorAuto(eventMarkerOutlineColor);
507 ✗ col = isColorAuto ? ImPlot::GetColormapColor(plotElementIdx) : eventMarkerOutlineColor;
508 ✗ if (ImGui::ColorEdit4("Event Marker Outline Color", &col.x))
509 {
510 ✗ eventMarkerOutlineColor = col;
511 ✗ ret.changed = true;
512 }
513 ✗ if (!isColorAuto)
514 {
515 ✗ ImGui::SameLine();
516 ✗ if (ImGui::Button("Auto##Event Marker Outline Color"))
517 {
518 ✗ eventMarkerOutlineColor = IMPLOT_AUTO_COL;
519 }
520 }
521
522 ✗ if (ImGui::InputText("Event Filter Regex", &eventTooltipFilterRegex))
523 {
524 ✗ eventMarker->clear();
525 ✗ eventTooltips->clear();
526 ✗ ret.changed = true;
527 }
528 }
529 }
530
531 ✗ ImPlot::EndLegendPopup();
532 }
533 ✗ return ret;
534 }
535
536 ✗ void PlotItemStyle::plotData(const char* plotName,
537 const ScrollingBuffer<double>& xData,
538 const ScrollingBuffer<double>& yData,
539 int plotElementIdx,
540 int defaultStride,
541 ImPlotLineFlags plotLineFlags,
542 ScrollingBuffer<ImU32>* colormapMaskColors,
543 ScrollingBuffer<ImU32>* markerColormapMaskColors,
544 const std::array<ScrollingBuffer<double>, 2>* yErrorData) const
545 {
546 ✗ auto lineColor = ImPlot::IsColorAuto(color) ? ImPlot::GetColormapColor(plotElementIdx) : color;
547 ✗ if (lineType == PlotItemStyle::LineType::Line)
548 {
549 ✗ ImPlot::SetNextLineStyle(lineColor, thickness);
550 }
551 ✗ if (lineType == PlotItemStyle::LineType::Scatter || markers)
552 {
553 ✗ ImPlot::SetNextMarkerStyle(markerStyle,
554 ✗ markerSize,
555 ✗ ImPlot::IsColorAuto(markerFillColor) ? ImPlot::GetColormapColor(plotElementIdx) : markerFillColor,
556 ✗ markerWeight,
557 ✗ ImPlot::IsColorAuto(markerOutlineColor) ? ImPlot::GetColormapColor(plotElementIdx) : markerOutlineColor);
558 }
559
560 ✗ auto stride = this->stride ? this->stride : defaultStride;
561 ✗ auto dataPointCount = static_cast<int>(std::ceil(static_cast<double>(yData.size())
562 ✗ / static_cast<double>(stride)));
563
564 // Plot the data
565 ✗ if (lineType == PlotItemStyle::LineType::Line)
566 {
567 ✗ if (colormapMaskColors && markerColormapMaskColors && colormapMask.first != ColormapMaskType::None)
568 {
569 ✗ ImPlot::SetNextColorsData(ImPlotCol_Line, colormapMaskColors->data(), stride * static_cast<int>(sizeof(ImU32)));
570 ✗ if (markers)
571 {
572 ✗ ImPlot::SetNextColorsData(ImPlotCol_MarkerFill, markerColormapMask.first != ColormapMaskType::None ? markerColormapMaskColors->data() : colormapMaskColors->data(), stride * static_cast<int>(sizeof(ImU32)));
573 ✗ ImPlot::SetNextColorsData(ImPlotCol_MarkerOutline, markerColormapMask.first != ColormapMaskType::None ? markerColormapMaskColors->data() : colormapMaskColors->data(), stride * static_cast<int>(sizeof(ImU32)));
574 }
575 }
576 ✗ else if (markerColormapMaskColors && markers && markerColormapMask.first != ColormapMaskType::None)
577 {
578 ✗ ImPlot::SetNextColorsData(ImPlotCol_MarkerFill, markerColormapMaskColors->data(), stride * static_cast<int>(sizeof(ImU32)));
579 ✗ ImPlot::SetNextColorsData(ImPlotCol_MarkerOutline, markerColormapMaskColors->data(), stride * static_cast<int>(sizeof(ImU32)));
580 }
581 ✗ ImPlot::PlotLine(plotName,
582 xData.data(),
583 yData.data(),
584 dataPointCount,
585 lineFlags.value_or(plotLineFlags),
586 ✗ static_cast<int>(std::ceil(static_cast<double>(yData.offset()) / static_cast<double>(stride))),
587 stride * static_cast<int>(sizeof(double)));
588
589 ✗ if (errorBoundsEnabled && yErrorData && ImPlot::GetCurrentPlot()->Items.GetItemByIndex(plotElementIdx)->Show)
590 {
591 ✗ ImPlot::SetNextFillStyle(lineColor, errorBoundsAlpha);
592 ✗ ImPlot::PlotShaded("",
593 xData.data(),
594 ✗ (*yErrorData)[0].data(),
595 ✗ (*yErrorData)[1].data(),
596 dataPointCount,
597 ImPlotShadedFlags_None,
598 ✗ static_cast<int>(std::ceil(static_cast<double>(yData.offset()) / static_cast<double>(stride))),
599 stride * static_cast<int>(sizeof(double)));
600 }
601 }
602 ✗ else if (lineType == PlotItemStyle::LineType::Scatter)
603 {
604 ✗ if (colormapMaskColors && colormapMask.first != ColormapMaskType::None && colormapMaskColors->isInfiniteBuffer())
605 {
606 ✗ ImPlot::SetNextColorsData(ImPlotCol_MarkerFill, colormapMaskColors->data(), stride * static_cast<int>(sizeof(ImU32)));
607 ✗ ImPlot::SetNextColorsData(ImPlotCol_MarkerOutline, colormapMaskColors->data(), stride * static_cast<int>(sizeof(ImU32)));
608 }
609 ✗ ImPlot::PlotScatter(plotName,
610 xData.data(),
611 yData.data(),
612 dataPointCount,
613 ✗ lineFlags.value_or(plotLineFlags) & ImPlotLineFlags_NoClip ? ImPlotScatterFlags_NoClip : ImPlotScatterFlags_None,
614 ✗ static_cast<int>(std::ceil(static_cast<double>(yData.offset()) / static_cast<double>(stride))),
615 stride * static_cast<int>(sizeof(double)));
616 }
617 ✗ }
618
619 } // namespace NAV
620