1
0
Fichiers
Modulations_numeriques/imgui_utils.cxx

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));
}