mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-24 01:31:46 +00:00
[sim] Add plotting to simulation GUI
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2017-2019 FIRST. All Rights Reserved. */
|
||||
/* Copyright (c) 2017-2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace halsimgui {
|
||||
|
||||
class GuiDataSource;
|
||||
|
||||
/**
|
||||
* DrawLEDs() configuration for 2D arrays.
|
||||
*/
|
||||
@@ -60,4 +62,28 @@ void DrawLEDs(const int* values, int numValues, int cols, const ImU32* colors,
|
||||
float size = 0.0f, float spacing = 0.0f,
|
||||
const LEDConfig& config = LEDConfig{});
|
||||
|
||||
/**
|
||||
* Draw a 2D array of LEDs.
|
||||
*
|
||||
* Values are indices into colors array. Positive values are filled (lit),
|
||||
* negative values are unfilled (dark / border only). The actual color index
|
||||
* is the absolute value of the value - 1. 0 values are not drawn at all
|
||||
* (an empty space is left).
|
||||
*
|
||||
* @param values values array
|
||||
* @param sources sources array
|
||||
* @param numValues size of values and sources arrays
|
||||
* @param cols number of columns
|
||||
* @param colors colors array
|
||||
* @param size size of each LED (both horizontal and vertical);
|
||||
* if 0, defaults to 1/2 of font size
|
||||
* @param spacing spacing between each LED (both horizontal and vertical);
|
||||
* if 0, defaults to 1/3 of font size
|
||||
* @param config 2D array configuration
|
||||
*/
|
||||
void DrawLEDSources(const int* values, GuiDataSource** sources, int numValues,
|
||||
int cols, const ImU32* colors, float size = 0.0f,
|
||||
float spacing = 0.0f,
|
||||
const LEDConfig& config = LEDConfig{});
|
||||
|
||||
} // namespace halsimgui
|
||||
|
||||
186
simulation/halsim_gui/src/main/native/include/GuiDataSource.h
Normal file
186
simulation/halsim_gui/src/main/native/include/GuiDataSource.h
Normal file
@@ -0,0 +1,186 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <wpi/Signal.h>
|
||||
#include <wpi/StringRef.h>
|
||||
#include <wpi/Twine.h>
|
||||
#include <wpi/spinlock.h>
|
||||
|
||||
namespace halsimgui {
|
||||
|
||||
/**
|
||||
* A data source.
|
||||
*/
|
||||
class GuiDataSource {
|
||||
public:
|
||||
explicit GuiDataSource(const wpi::Twine& id);
|
||||
GuiDataSource(const wpi::Twine& id, int index);
|
||||
GuiDataSource(const wpi::Twine& id, int index, int index2);
|
||||
~GuiDataSource();
|
||||
|
||||
GuiDataSource(const GuiDataSource&) = delete;
|
||||
GuiDataSource& operator=(const GuiDataSource&) = delete;
|
||||
|
||||
const char* GetId() const { return m_id.c_str(); }
|
||||
|
||||
void SetName(const wpi::Twine& name) { m_name = name.str(); }
|
||||
const char* GetName() const { return m_name.c_str(); }
|
||||
|
||||
void SetDigital(bool digital) { m_digital = digital; }
|
||||
bool IsDigital() const { return m_digital; }
|
||||
|
||||
void SetValue(double value) {
|
||||
m_value = value;
|
||||
valueChanged(value);
|
||||
}
|
||||
double GetValue() const { return m_value; }
|
||||
|
||||
// drag source helpers
|
||||
void LabelText(const char* label, const char* fmt, ...) const;
|
||||
void LabelTextV(const char* label, const char* fmt, va_list args) const;
|
||||
bool Combo(const char* label, int* current_item, const char* const items[],
|
||||
int items_count, int popup_max_height_in_items = -1) const;
|
||||
bool SliderFloat(const char* label, float* v, float v_min, float v_max,
|
||||
const char* format = "%.3f", float power = 1.0f) const;
|
||||
bool InputDouble(const char* label, double* v, double step = 0.0,
|
||||
double step_fast = 0.0, const char* format = "%.6f",
|
||||
ImGuiInputTextFlags flags = 0) const;
|
||||
bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100,
|
||||
ImGuiInputTextFlags flags = 0) const;
|
||||
void EmitDrag(ImGuiDragDropFlags flags = 0) const;
|
||||
|
||||
wpi::sig::SignalBase<wpi::spinlock, double> valueChanged;
|
||||
|
||||
static GuiDataSource* Find(wpi::StringRef id);
|
||||
|
||||
static wpi::sig::Signal<const char*, GuiDataSource*> sourceCreated;
|
||||
|
||||
private:
|
||||
std::string m_id;
|
||||
std::string m_name;
|
||||
bool m_digital = false;
|
||||
std::atomic<double> m_value = 0;
|
||||
};
|
||||
|
||||
} // namespace halsimgui
|
||||
|
||||
#define HALSIMGUI_DATASOURCE(cbname, id, TYPE, vtype) \
|
||||
class cbname##Source : public ::halsimgui::GuiDataSource { \
|
||||
public: \
|
||||
cbname##Source() \
|
||||
: GuiDataSource(id), \
|
||||
m_callback{ \
|
||||
HALSIM_Register##cbname##Callback(CallbackFunc, this, true)} { \
|
||||
SetDigital(HAL_##TYPE == HAL_BOOLEAN); \
|
||||
} \
|
||||
\
|
||||
~cbname##Source() { \
|
||||
if (m_callback != 0) HALSIM_Cancel##cbname##Callback(m_callback); \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
static void CallbackFunc(const char*, void* param, \
|
||||
const HAL_Value* value) { \
|
||||
if (value->type == HAL_##TYPE) \
|
||||
static_cast<cbname##Source*>(param)->SetValue(value->data.v_##vtype); \
|
||||
} \
|
||||
\
|
||||
int32_t m_callback; \
|
||||
}
|
||||
|
||||
#define HALSIMGUI_DATASOURCE_BOOLEAN(cbname, id) \
|
||||
HALSIMGUI_DATASOURCE(cbname, id, BOOLEAN, boolean)
|
||||
|
||||
#define HALSIMGUI_DATASOURCE_DOUBLE(cbname, id) \
|
||||
HALSIMGUI_DATASOURCE(cbname, id, DOUBLE, double)
|
||||
|
||||
#define HALSIMGUI_DATASOURCE_INT(cbname, id) \
|
||||
HALSIMGUI_DATASOURCE(cbname, id, INT, int)
|
||||
|
||||
#define HALSIMGUI_DATASOURCE_INDEXED(cbname, id, TYPE, vtype) \
|
||||
class cbname##Source : public ::halsimgui::GuiDataSource { \
|
||||
public: \
|
||||
explicit cbname##Source(int32_t index, int channel = -1) \
|
||||
: GuiDataSource(id, channel < 0 ? index : channel), \
|
||||
m_index{index}, \
|
||||
m_channel{channel < 0 ? index : channel}, \
|
||||
m_callback{HALSIM_Register##cbname##Callback(index, CallbackFunc, \
|
||||
this, true)} { \
|
||||
SetDigital(HAL_##TYPE == HAL_BOOLEAN); \
|
||||
} \
|
||||
\
|
||||
~cbname##Source() { \
|
||||
if (m_callback != 0) \
|
||||
HALSIM_Cancel##cbname##Callback(m_index, m_callback); \
|
||||
} \
|
||||
\
|
||||
int32_t GetIndex() const { return m_index; } \
|
||||
\
|
||||
int GetChannel() const { return m_channel; } \
|
||||
\
|
||||
private: \
|
||||
static void CallbackFunc(const char*, void* param, \
|
||||
const HAL_Value* value) { \
|
||||
if (value->type == HAL_##TYPE) \
|
||||
static_cast<cbname##Source*>(param)->SetValue(value->data.v_##vtype); \
|
||||
} \
|
||||
\
|
||||
int32_t m_index; \
|
||||
int m_channel; \
|
||||
int32_t m_callback; \
|
||||
}
|
||||
|
||||
#define HALSIMGUI_DATASOURCE_BOOLEAN_INDEXED(cbname, id) \
|
||||
HALSIMGUI_DATASOURCE_INDEXED(cbname, id, BOOLEAN, boolean)
|
||||
|
||||
#define HALSIMGUI_DATASOURCE_DOUBLE_INDEXED(cbname, id) \
|
||||
HALSIMGUI_DATASOURCE_INDEXED(cbname, id, DOUBLE, double)
|
||||
|
||||
#define HALSIMGUI_DATASOURCE_INDEXED2(cbname, id, TYPE, vtype) \
|
||||
class cbname##Source : public ::halsimgui::GuiDataSource { \
|
||||
public: \
|
||||
explicit cbname##Source(int32_t index, int32_t channel) \
|
||||
: GuiDataSource(id, index, channel), \
|
||||
m_index{index}, \
|
||||
m_channel{channel}, \
|
||||
m_callback{HALSIM_Register##cbname##Callback( \
|
||||
index, channel, CallbackFunc, this, true)} { \
|
||||
SetDigital(HAL_##TYPE == HAL_BOOLEAN); \
|
||||
} \
|
||||
\
|
||||
~cbname##Source() { \
|
||||
if (m_callback != 0) \
|
||||
HALSIM_Cancel##cbname##Callback(m_index, m_channel, m_callback); \
|
||||
} \
|
||||
\
|
||||
int32_t GetIndex() const { return m_index; } \
|
||||
\
|
||||
int32_t GetChannel() const { return m_channel; } \
|
||||
\
|
||||
private: \
|
||||
static void CallbackFunc(const char*, void* param, \
|
||||
const HAL_Value* value) { \
|
||||
if (value->type == HAL_##TYPE) \
|
||||
static_cast<cbname##Source*>(param)->SetValue(value->data.v_##vtype); \
|
||||
} \
|
||||
\
|
||||
int32_t m_index; \
|
||||
int32_t m_channel; \
|
||||
int32_t m_callback; \
|
||||
}
|
||||
|
||||
#define HALSIMGUI_DATASOURCE_BOOLEAN_INDEXED2(cbname, id) \
|
||||
HALSIMGUI_DATASOURCE_INDEXED2(cbname, id, BOOLEAN, boolean)
|
||||
|
||||
#define HALSIMGUI_DATASOURCE_DOUBLE_INDEXED2(cbname, id) \
|
||||
HALSIMGUI_DATASOURCE_INDEXED2(cbname, id, DOUBLE, double)
|
||||
@@ -18,16 +18,24 @@ class NameInfo {
|
||||
|
||||
bool HasName() const { return m_name[0] != '\0'; }
|
||||
const char* GetName() const { return m_name; }
|
||||
void GetName(char* buf, size_t size, const char* defaultName);
|
||||
void GetName(char* buf, size_t size, const char* defaultName, int index);
|
||||
void GetName(char* buf, size_t size, const char* defaultName) const;
|
||||
void GetName(char* buf, size_t size, const char* defaultName,
|
||||
int index) const;
|
||||
void GetName(char* buf, size_t size, const char* defaultName, int index,
|
||||
int index2);
|
||||
int index2) const;
|
||||
void GetLabel(char* buf, size_t size, const char* defaultName) const;
|
||||
void GetLabel(char* buf, size_t size, const char* defaultName,
|
||||
int index) const;
|
||||
void GetLabel(char* buf, size_t size, const char* defaultName, int index,
|
||||
int index2) const;
|
||||
|
||||
bool ReadIni(wpi::StringRef name, wpi::StringRef value);
|
||||
void WriteIni(ImGuiTextBuffer* out);
|
||||
void PushEditNameId(int index);
|
||||
void PushEditNameId(const char* name);
|
||||
void PopupEditName(int index);
|
||||
void PopupEditName(const char* name);
|
||||
bool PopupEditName(int index);
|
||||
bool PopupEditName(const char* name);
|
||||
bool InputTextName(const char* label_id, ImGuiInputTextFlags flags = 0);
|
||||
|
||||
private:
|
||||
char m_name[64];
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
|
||||
namespace halsimgui {
|
||||
|
||||
template <typename Info>
|
||||
class IniSaverVector : public std::vector<Info> {
|
||||
public:
|
||||
explicit IniSaverVector(const char* typeName) : m_typeName(typeName) {}
|
||||
void Initialize();
|
||||
|
||||
private:
|
||||
static void* ReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler,
|
||||
const char* name);
|
||||
static void ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler,
|
||||
void* entry, const char* lineStr);
|
||||
static void WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler,
|
||||
ImGuiTextBuffer* out_buf);
|
||||
|
||||
const char* m_typeName;
|
||||
};
|
||||
|
||||
} // namespace halsimgui
|
||||
|
||||
#include "IniSaverVector.inl"
|
||||
@@ -0,0 +1,60 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace halsimgui {
|
||||
|
||||
template <typename Info>
|
||||
void IniSaverVector<Info>::Initialize() {
|
||||
// hook ini handler to save settings
|
||||
ImGuiSettingsHandler iniHandler;
|
||||
iniHandler.TypeName = m_typeName;
|
||||
iniHandler.TypeHash = ImHashStr(m_typeName);
|
||||
iniHandler.ReadOpenFn = ReadOpen;
|
||||
iniHandler.ReadLineFn = ReadLine;
|
||||
iniHandler.WriteAllFn = WriteAll;
|
||||
iniHandler.UserData = this;
|
||||
ImGui::GetCurrentContext()->SettingsHandlers.push_back(iniHandler);
|
||||
}
|
||||
|
||||
template <typename Info>
|
||||
void* IniSaverVector<Info>::ReadOpen(ImGuiContext* ctx,
|
||||
ImGuiSettingsHandler* handler,
|
||||
const char* name) {
|
||||
auto self = static_cast<IniSaverVector*>(handler->UserData);
|
||||
unsigned int num;
|
||||
if (wpi::StringRef{name}.getAsInteger(10, num)) return nullptr;
|
||||
if (num >= self->size()) self->resize(num + 1);
|
||||
return &(*self)[num];
|
||||
}
|
||||
|
||||
template <typename Info>
|
||||
void IniSaverVector<Info>::ReadLine(ImGuiContext* ctx,
|
||||
ImGuiSettingsHandler* handler, void* entry,
|
||||
const char* lineStr) {
|
||||
auto element = static_cast<Info*>(entry);
|
||||
wpi::StringRef line{lineStr};
|
||||
auto [name, value] = line.split('=');
|
||||
name = name.trim();
|
||||
value = value.trim();
|
||||
element->ReadIni(name, value);
|
||||
}
|
||||
|
||||
template <typename Info>
|
||||
void IniSaverVector<Info>::WriteAll(ImGuiContext* ctx,
|
||||
ImGuiSettingsHandler* handler,
|
||||
ImGuiTextBuffer* out_buf) {
|
||||
auto self = static_cast<IniSaverVector*>(handler->UserData);
|
||||
for (size_t i = 0; i < self->size(); ++i) {
|
||||
out_buf->appendf("[%s][%d]\n", self->m_typeName, static_cast<int>(i));
|
||||
(*self)[i].WriteIni(out_buf);
|
||||
out_buf->append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace halsimgui
|
||||
@@ -1,5 +1,5 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
@@ -35,6 +35,8 @@ void HALSIMGUI_DeviceTreeFinishDevice(void);
|
||||
|
||||
namespace halsimgui {
|
||||
|
||||
class GuiDataSource;
|
||||
|
||||
class SimDeviceGui {
|
||||
public:
|
||||
static void Initialize();
|
||||
@@ -69,6 +71,22 @@ class SimDeviceGui {
|
||||
const char** options = nullptr,
|
||||
int32_t numOptions = 0);
|
||||
|
||||
/**
|
||||
* Displays device value formatted the same way as SimDevice device values.
|
||||
*
|
||||
* @param name value name
|
||||
* @param readonly prevent value from being modified by the user
|
||||
* @param value value contents (modified in place)
|
||||
* @param source data source (may be nullptr)
|
||||
* @param options options array for enum values
|
||||
* @param numOptions size of options array for enum values
|
||||
* @return True if value was modified by the user
|
||||
*/
|
||||
static bool DisplayValueSource(const char* name, bool readonly,
|
||||
HAL_Value* value, const GuiDataSource* source,
|
||||
const char** options = nullptr,
|
||||
int32_t numOptions = 0);
|
||||
|
||||
/**
|
||||
* Wraps ImGui::CollapsingHeader() to provide consistency and open
|
||||
* persistence. As with the ImGui function, returns true if the tree node
|
||||
|
||||
Reference in New Issue
Block a user