276 lignes
8.2 KiB
C++
276 lignes
8.2 KiB
C++
/* ImGui utils */
|
|
|
|
/* Fichier contenant quelques fonctionnalités basées sur ImGui :
|
|
* VToggleButton() dérivé de ToggleButton
|
|
* DrawVToggleButton
|
|
*
|
|
* Copyright Eric Bachard 2026/01/14 18h20
|
|
* License GPL v2
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <cmath>
|
|
#include <string>
|
|
|
|
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
|
#endif
|
|
|
|
#include "digital_modulation_fr.hpp"
|
|
#include "imgui.h"
|
|
#include "vtoggle_button.h"
|
|
#include "imgui_utils.h"
|
|
#include "imgui_themes.h"
|
|
|
|
#ifdef M_PI
|
|
#undef M_PI
|
|
#define M_PI 3.14159265358979323846f
|
|
#endif
|
|
|
|
void selectThemeMenu(Application * p_app)
|
|
{
|
|
static THEME selected_menutheme = p_app->get_current_theme();
|
|
|
|
if (ImGui::BeginMenu(THEME_MENU_ENTRY))
|
|
{
|
|
if (ImGui::Selectable(TRADITIONAL_GREEN_THEME_MENU_ENTRY))
|
|
selected_menutheme = LIGHT_GREEN_THEME;
|
|
|
|
if (ImGui::Selectable(DARK_THEME_MENU_ENTRY))
|
|
selected_menutheme = DARK_THEME;
|
|
|
|
if (ImGui::Selectable(CLASSIC_THEME_MENU_ENTRY))
|
|
selected_menutheme = CLASSIC_THEME;
|
|
|
|
if (ImGui::Selectable(LIGHT_BLUE_THEME_MENU_ENTRY))
|
|
selected_menutheme = LIGHT_BLUE_THEME;
|
|
|
|
if (ImGui::Selectable(WINDOWS_THEME_MENU_ENTRY))
|
|
selected_menutheme = WINDOWS_THEME;
|
|
|
|
if (selected_menutheme != p_app->get_current_theme())
|
|
p_app->setTheme(selected_menutheme);
|
|
|
|
ImGui::EndMenu();
|
|
}
|
|
}
|
|
|
|
void selectFrameSize(DigitalModulation * p_Dmod)
|
|
{
|
|
// by default, we recopy the current value
|
|
int frame_length = p_Dmod->getWordSize();
|
|
|
|
if (ImGui::BeginMenu(FRAME_SIZE))
|
|
{
|
|
if (ImGui::Selectable(HEIGHT_BITS_FRAME_SIZE))
|
|
frame_length = 8;
|
|
|
|
if (ImGui::Selectable(SIXTEEN_BITS_FRAME_SIZE))
|
|
frame_length = 16;
|
|
|
|
if (ImGui::Selectable(TWENTY_FOUR_BITS_FRAME_SIZE))
|
|
frame_length = 24;
|
|
|
|
if (frame_length != p_Dmod->getWordSize())
|
|
p_Dmod->setWordSize(frame_length);
|
|
|
|
ImGui::EndMenu();
|
|
}
|
|
}
|
|
|
|
|
|
// ToogleButton transformé en VToggleButton
|
|
bool VToggleButton(const char* str_id, bool* v)
|
|
{
|
|
ImVec2 p = ImGui::GetCursorScreenPos();
|
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
|
|
|
// Taille du toggle
|
|
// FIXME : elaborate
|
|
float width = ImGui::GetFrameHeight();
|
|
//float width = VTOGGLEBUTTON_WIDTH;
|
|
float height = width * 1.8f;
|
|
float radius = width * 0.5f;
|
|
|
|
ImGui::InvisibleButton(str_id, ImVec2(width, height));
|
|
|
|
// Vérifie si le bouton a été cliqué
|
|
bool clicked = ImGui::IsItemClicked();
|
|
if (clicked)
|
|
*v = !*v;
|
|
|
|
// Choix de la couleur de fond selon l'état
|
|
ImU32 col_bg = ImGui::IsItemHovered()
|
|
? (*v ? IM_COL32(165,231,88,255) : IM_COL32(200,200,200,255)) //ON
|
|
: (*v ? IM_COL32(145,211,68,255) : IM_COL32(218,218,218,255)); //OFF
|
|
|
|
draw_list->AddRectFilled( p, ImVec2(p.x + width, p.y + height), col_bg, radius ); // Dessine le rectangle du Toggle
|
|
float cy = *v ? (p.y + radius) : (p.y + height - radius); // Position verticale du petit rond
|
|
draw_list->AddCircleFilled( ImVec2(p.x + radius, cy), radius - 2.0f, IM_COL32(255,255,255,255) ); // Dessine le rond blanc du Toggle
|
|
|
|
return clicked;
|
|
}
|
|
|
|
void drawVToggleButtons(int * dm_bits, int wordSize)
|
|
{
|
|
// CHILD2_WIDTH = 300.0f
|
|
float drawSize = ImGui::GetContentRegionAvail().x - CHILD2_WIDTH;
|
|
|
|
// FIXME : calculer sérieusement ces valeurs
|
|
//TEST
|
|
|
|
// La formule :
|
|
// draw_size = (P+B)wordSize + P + marges // Il y a 1 padding de plus que de boutons dessinés
|
|
//
|
|
// 20.0f because fo the left/right margins
|
|
// On dessine dans la child window 1,
|
|
// et le nombre de pixels n'est plus le même en plein écran
|
|
// exemple : drawSize = 664.0f contre 1304.0f en plein écran.
|
|
// 2 solutions : soit dessiner a valeur définie et testée (pas très pro)
|
|
// soit calculer précisément le padding. Or la formule ne semble pas fonctionner ...
|
|
// La valeur de 20.0f est soustraite car il faut tenir compte d'une marge à gauche
|
|
// et d'une marge à droite pour le canvas.
|
|
// current best value : 20.0f
|
|
float padding = ((drawSize - 20.0f) - VTOGGLEBUTTON_WIDTH * (float)wordSize)/((float)wordSize + 1);
|
|
|
|
// hack ... le temps de corriger la formule
|
|
if (wordSize == 8)
|
|
padding += 32.0f;
|
|
|
|
if (wordSize == 16)
|
|
padding += 7.5f;
|
|
|
|
if (drawSize > 1000.0F)
|
|
{
|
|
if (wordSize == 8)
|
|
padding += 7.3f; // best value 7.3f
|
|
|
|
if (wordSize == 16)
|
|
padding += 2.3f; // best value 2.3f
|
|
else
|
|
padding += 1.2f; // best value 1.2f
|
|
}
|
|
// end hack
|
|
|
|
ImGui::Dummy(ImVec2(padding / 2.0f, 0.0f)); ImGui::SameLine(); // Padding pour le premier bouton
|
|
|
|
for (int i = 0; i < wordSize ; i++)
|
|
{
|
|
ImGui::PushID(i);
|
|
bool bit = (dm_bits[i] != 0);
|
|
|
|
if (VToggleButton("##Button", &bit))
|
|
dm_bits[i] = bit ? 1 : 0;
|
|
ImGui::PopID();
|
|
|
|
if (i < (wordSize - 1)) // Pas de padding pour le dernier
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Dummy(ImVec2(padding, 0.0f)); ImGui::SameLine();
|
|
}
|
|
}
|
|
// debug purpose
|
|
//#define DEBUG_ME
|
|
#ifdef DEBUG_ME
|
|
ImGui::Text(" wordSize : %d", wordSize);
|
|
ImGui::SameLine();
|
|
ImGui::Text(" padding : %f", padding);
|
|
ImGui::SameLine();
|
|
ImGui::Text(" drawSize : %f", drawSize);
|
|
ImGui::SameLine();
|
|
ImGui::Text("VTOGGLEBUTTON_WIDTH : %f", VTOGGLEBUTTON_WIDTH);
|
|
#endif /* DEBUG_ME */
|
|
}
|
|
|
|
void highlightFrame(int * dm_bits, int wordSize)
|
|
{
|
|
ImVec2 text_pos = ImGui::GetCursorScreenPos();
|
|
// pour avoir la surbrillance de longueur variable
|
|
std::string text_trame = "Trame :";
|
|
|
|
for (int i = 0; i < wordSize; i++)
|
|
{
|
|
if (0 == i % 8 && i != 0) // Découpage par octets
|
|
text_trame += " ";
|
|
|
|
text_trame += " ";
|
|
text_trame += std::to_string(dm_bits[i]);
|
|
}
|
|
|
|
ImVec2 text_size = ImGui::CalcTextSize(text_trame.c_str());
|
|
ImGui::GetWindowDrawList()->AddRectFilled(
|
|
text_pos,
|
|
ImVec2(text_pos.x + text_size.x, text_pos.y + text_size.y),
|
|
IM_COL32(255, 255, 0, 128) // Jaune semi-transparent
|
|
);
|
|
ImGui::Text("%s", text_trame.c_str());
|
|
ImGui::NewLine();
|
|
ImGui::Separator();
|
|
}
|
|
|
|
// Helper to draw a small modulation example (Témoin visuel). Code proposé par
|
|
void DrawIndicator(const char * label,
|
|
float * carrier_freq,
|
|
DigitalModulationType * p_modulation_type,
|
|
int * samples_per_bit,
|
|
float amp_scale,
|
|
float phase_offset,
|
|
ImU32 color,
|
|
ImVec2 size)
|
|
{
|
|
ImGui::Text("%s", label);
|
|
ImVec2 p = ImGui::GetCursorScreenPos();
|
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
|
|
|
// Fond gris clair pour le témoin
|
|
draw_list->AddRectFilled(p, ImVec2(p.x + size.x, p.y + size.y), IM_COL32(245, 245, 245, 255));
|
|
|
|
// Bordure
|
|
draw_list->AddRect(p, ImVec2(p.x + size.x, p.y + size.y), IM_COL32(180, 180, 180, 255));
|
|
|
|
float cy = p.y + size.y * 0.5f; // Centre vertical
|
|
float base_amp = (size.y * 0.35f); // Amplitude de base (laisse une marge)
|
|
|
|
ImVec2 prev;
|
|
bool first = true;
|
|
|
|
// On dessine 2 périodes (ou le double si un symbole est codé sur 2 bits),
|
|
// pour bien visualiser la forme
|
|
static float k2 = 1.0f;
|
|
|
|
switch (*p_modulation_type)
|
|
{
|
|
case MOD_MASK_TYPE :
|
|
case MOD_M_QAM_TYPE:
|
|
case MOD_4_QAM_TYPE:
|
|
case MOD_MFSK_TYPE :
|
|
k2 = 2.0f;
|
|
break;
|
|
|
|
default:
|
|
k2 = 1.0f;
|
|
break;
|
|
}
|
|
|
|
// tracé de la courbe
|
|
for (int i = 0; i <= *samples_per_bit; i++)
|
|
{
|
|
float t = (float)i / (float)*samples_per_bit; // 0 à 1
|
|
// Formule du signal : sin(2*PI * f * t + phase)
|
|
float y = cy - sinf(k2 * 2.0f * M_PI * (* carrier_freq) * t + phase_offset) * (base_amp * amp_scale);
|
|
float x = p.x + t * size.x;
|
|
ImVec2 curr(x, y);
|
|
|
|
if (!first)
|
|
draw_list->AddLine(prev, curr, color, 1.5f); // Ligne un peu plus épaisse
|
|
prev = curr;
|
|
first = false;
|
|
}
|
|
|
|
// Réserve l'espace pour qu'ImGui ne dessine pas par dessus
|
|
ImGui::Dummy(size);
|
|
// Petit espacement vertical après
|
|
ImGui::Dummy(ImVec2(0, 5));
|
|
}
|
|
|