GrapichsByLeo/Histogram/Histograma/HistogramaVer.mqh

548 lines
39 KiB
MQL5
Raw Permalink Normal View History

2025-10-13 07:15:19 -05:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| Vertical.mqh |
//| Copyright 2025, Niquel Mendoza. |
//| https://www.mql5.com/es/users/nique_372/news |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Niquel Mendoza."
#property link "https://www.mql5.com/es/users/nique_372/news"
#property strict
#include "HistogramaBases.mqh"
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#define HISTOGRAM_CALCULE_ANCHO(v,m) int((v)*(m))
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CHistogramVertical : public CHistogram
{
private:
void RecalculeCordinatesBarsAndText() override final;
void OnNeweCordinatesBarsAndSize() override final;
void OnBarraAdd(int index) override final;
void OnBarrDelete(double extra_value) override final;
void OnSetLienzo() override final;
void OnMaxOrMinValueSuperate() override final;
void InitCordinates() override final;
void ConjuntoBarWidth(int index, double new_value) override final;
public:
CHistogramVertical() { m_type = HISTOGRAM_VERTICAL; }
int AddConjuntoBar(const string &label, uint label_clr, int label_fontsize, const string &label_font, ENUM_HISTOGRAM_TEXT_MODE label_mode, int barras_de_reserva_, int eje_gap,
ENUM_HIST_DRAW_RECT_STYLE initial_style, double width = 0.000000) override final;
using CHistogram::AddConjuntoBar; // Desocultamos la funcion soibrecatgada de AddCounjtoBar con clr aleatorio
void MinConjuntoAncho(int new_value) override;
2025-10-14 10:58:19 -05:00
void CreateEjeLine(int mode_position_line_sections, uint clr_line_Sections, uint clr_line_fixed, int initial_sections,
int section_gap_value, int sections_size_px, int8_t section_decimals, int section_fontsize = 0, string section_font = NULL) override;
void NumConjuntoBars(int num_bars, int reserve_bars = 0) override;
using CHistogram::NumConjuntoBars; // Descoultamos el meotodo getter
2025-10-13 07:15:19 -05:00
};
//+------------------------------------------------------------------+
//| Resize al numero de barras |
//+------------------------------------------------------------------+
// Unicamente resize el tama<EFBFBD>o de berras
// Para poder setear las barras use SetBar con redraw false (todavia no se le asigna cordenadas)
// O tambien SetBarFast
void CHistogramVertical::NumConjuntoBars(int num_bars, int reserve_bars = 0)
{
m_bars_size = num_bars;
ArrayResize(m_bars, m_bars_size);
for(int i = 0; i < m_bars_size; i++)
{
m_bars[i] = new CHistogramConjuntoVertical();
m_bars[i].ReserveBarras(reserve_bars);
m_bars[i].CleanColor(m_back_color);
}
m_curr_index_bar_p = 0;
}
2025-10-13 07:15:19 -05:00
2025-10-14 10:58:19 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CHistogramVertical::CreateEjeLine(int mode_position_line_sections, uint clr_line_Sections, uint clr_line_fixed, int initial_sections,
int section_gap_value, int sections_size_px, int8_t section_decimals, int section_fontsize = 0, string section_font = NULL)
2025-10-14 10:58:19 -05:00
{
// Apunta a algo, es seguro comprar con NULL, dado que al eliminar o iniciar el puntero tendra un valor de NULL (no hay objeto invalido, pero direccion seigue apuntado a algo)
if(m_eje != NULL)
2025-10-14 10:58:19 -05:00
return;
m_eje = new CHistogramEjeVertical();
2025-10-14 10:58:19 -05:00
int x2 = m_barras_x1 + m_barras_espacio_dibujable_width;
int y2 = m_barras_y1 + m_barras_espacio_dibujable_height;
m_eje.Init(m_canvas, m_barras_x1, x2, m_barras_y1, y2, mode_position_line_sections, clr_line_Sections, clr_line_fixed, m_back_color);
const bool use_change_val = (m_fr_change_value == m_fr_min_value) ? false : true;
m_eje.HistogramLinePointerGet().CreateSections(initial_sections, m_fr_max_value, m_fr_min_value, use_change_val, m_fr_change_value, section_gap_value, sections_size_px,
section_decimals, section_fontsize, section_font); // Solo crea seccion el titulo lo hace el usuario si es que desea
}
2025-10-13 07:15:19 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CHistogramVertical::MinConjuntoAncho(int new_value)
{
//---
m_barras_min_width = new_value;
//--- Verificamos si las barras cumple, si no la marcamos para su posterior eliminacion
int idx_to_Remove[];
double extra = 0.00;
for(int i = 0; i < m_bars_size; i++)
{
m_bars[i].CleanBarras(false);
m_bars[i].TextClean();
const double v = m_bars[i].ValueForAncho();
// Calcular con max_height (vertical)
const int a = HISTOGRAM_CALCULE_ANCHO(v, m_barras_max_height);
if(a < m_barras_min_width)
{
extra += v;
idx_to_Remove.Push(i);
}
}
//--- Eliminar conjuntos muy peque<EFBFBD>os
const int size_remove = ArraySize(idx_to_Remove);
if(size_remove > 0)
{
for(int i = 0; i < size_remove; i++)
{
const int k = idx_to_Remove[i];
m_bars[k].Destroy();
delete m_bars[k];
}
RemoveMultipleIndexes(m_bars, idx_to_Remove, 0);
m_bars_size -= size_remove;
}
else
{
return; // Todas las barras cumple, nada que hacer
}
//--- Redistribuir espacio extra
const double rest = extra / double(m_bars_size);
// Iterar en Y (vertical)
int y = m_barras_cordinate_init;
// gap_corte es distancia entre x1 y condernada de corte
const int gap_corte = m_barras_cordenanda_eje_corte - m_barras_x1;
for(int i = 0; i < m_bars_size; i++)
{
m_bars[i].ValueForAnchoAument(rest);
// Calcular alto con max_height
const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_height);
// Par<EFBFBD>metros correctos para vertical
m_bars[i].SetNewsCordenadasAndSize(m_barras_x1, y, a);
// Avanzar en Y
y += a + m_barras_espacio_entre;
}
}
//+------------------------------------------------------------------+
2025-10-13 07:15:19 -05:00
//| A<EFBFBD>ade un nuevo conjunto de barras |
//+------------------------------------------------------------------+
int CHistogramVertical::AddConjuntoBar(const string &label, uint label_clr, int label_fontsize, const string &label_font, ENUM_HISTOGRAM_TEXT_MODE label_mode, int barras_de_reserva_, int eje_gap,
ENUM_HIST_DRAW_RECT_STYLE initial_style, double width = 0.000000)
{
m_curr_index_bar_p = m_bars_size;
ArrayResize(m_bars, m_bars_size + 1);
m_bars[m_bars_size] = new CHistogramConjuntoVertical(); // Cambiar esto, talvez com template.. <>
m_bars[m_bars_size].ReserveBarras(barras_de_reserva_);
m_bars[m_bars_size].Set(m_canvas, label, label_clr, label_mode, label_fontsize, label_font, eje_gap, initial_style);
m_bars[m_bars_size].ValueForAncho(width);
m_bars[m_bars_size].CleanColor(m_back_color); // Color de limpeiza que sea el fondo del lienzo
m_bars_size++;
return m_curr_index_bar_p;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CHistogramVertical::InitCordinates(void)
{
// Posici<EFBFBD>n Y inicial (ya incluye gap_init)
int y = m_barras_cordinate_init;
// gap_corte es distancia entre x1 y condernada de corte
const int gap_corte = m_barras_cordenanda_eje_corte - m_barras_x1;
// Iterar sobre cada conjunto de barras
for(int i = 0; i < m_bars_size; i++)
{
// Calcular alto del conjunto basado en su proporci<EFBFBD>n
// ValueForAncho() retorna proporci<EFBFBD>n (0.0-1.0)
// m_barras_max_height es el espacio vertical total disponible
const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_height);
// Inicializar conjunto con sus coordenadas
m_bars[i].Init(
m_function_value_to_pixel, // Funci<EFBFBD>n conversi<EFBFBD>n valor<EFBFBD>!pixel
m_barras_x1, // X1: borde izquierdo del <EFBFBD>rea
y, // Y: posici<EFBFBD>n vertical actual
gap_corte, //Gap del corte (pos<EFBFBD>!neg)
2025-10-13 07:15:19 -05:00
a, // Alto del conjunto en pixeles
m_factor_conversion_value_to_pixel_pos, // Factor para valores positivos
m_factor_conversion_value_to_pixel_neg, // Factor para valores negativos
m_fr_change_value // Valor donde ocurre el corte
);
// Avanzar a la siguiente posici<EFBFBD>n Y
// = posici<EFBFBD>n actual + alto del conjunto + espacio entre conjuntos
y += a + m_barras_espacio_entre;
}
}
//+------------------------------------------------------------------+
//| Configura el lienzo (VERTICAL) |
//+------------------------------------------------------------------+
void CHistogramVertical::OnSetLienzo(void)
{
// En vertical, los conjuntos se distribuyen en ALTURA (eje Y)
m_barras_max_height = m_barras_espacio_dibujable_height;
m_barras_max_height -= (m_bars_size - 1) * m_barras_espacio_entre; // Restamos gaps entre conjuntos
m_barras_max_height -= (m_barras_gap_init + m_barras_gap_end); // Restamos gaps inicial y final
// Las barras dentro de cada conjunto crecen en ANCHO (eje X)
m_barras_max_width = m_barras_espacio_dibujable_width;
//--- Calcular proporciones default para conjuntos sin ancho especificado
int idx_no_tiene[];
double sum = 0.00;
for(int i = 0; i < m_bars_size; i++)
{
const double v = m_bars[i].ValueForAncho();
if(v == 0.00)
idx_no_tiene.Push(i);
else
sum += v;
}
//--- Tama<EFBFBD>o del array
const int s = ArraySize(idx_no_tiene);
//--- No hay defaults, todos tienen proporci<EFBFBD>n definida
if(s < 1)
return;
//--- Calcular y asignar proporci<EFBFBD>n default
const double def_factor = (1.0 - sum) / double(s);
for(int i = 0; i < s; i++)
m_bars[idx_no_tiene[i]].ValueForAncho(def_factor);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CHistogramVertical::RecalculeCordinatesBarsAndText(void)
{
for(int i = 0; i < m_bars_size; i++)
m_bars[i].RecalculeCordinates();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CHistogramVertical::OnMaxOrMinValueSuperate(void)
{
// gap_corte es distancia entre x1 y condernada de corte
const int gap_corte = m_barras_cordenanda_eje_corte - m_barras_x1;
for(int i = 0; i < m_bars_size; i++)
{
m_bars[i].SetNewsValues(gap_corte,
m_factor_conversion_value_to_pixel_pos,
m_factor_conversion_value_to_pixel_neg,
m_fr_change_value);
}
}
//+------------------------------------------------------------------+
//| Recalcula coordenadas y tama<EFBFBD>os (VERTICAL) |
//+------------------------------------------------------------------+
void CHistogramVertical::OnNeweCordinatesBarsAndSize(void)
{
// Iterar en Y (vertical), no en X
int y = m_barras_cordinate_init;
for(int i = 0; i < m_bars_size; i++)
{
// Calcular ALTO usando max_height
const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_height);
// Par<EFBFBD>metros correctos (x_fijo, y_variable, alto)
m_bars[i].SetNewsCordenadasAndSize(m_barras_x1, y, a);
// Avanzar en Y
y += a + m_barras_espacio_entre;
}
}
//+------------------------------------------------------------------+
//| Se ejecuta al a<EFBFBD>adir un conjunto (VERTICAL) |
//+------------------------------------------------------------------+
void CHistogramVertical::OnBarraAdd(int index)
{
// Restar espacio de HEIGHT (no width)
m_barras_max_height -= m_barras_espacio_entre;
double extra = m_bars[index].ValueForAncho();
if(extra == 0.00)
{
extra = 1.0 / double(m_bars_size);
}
double rest = extra / double(m_bars_size - 1);
//--- Reducir proporci<EFBFBD>n de otros conjuntos
int idx_to_Remove[];
extra = 0.00;
for(int i = 0; i < m_bars_size; i++)
{
m_bars[i].CleanBarras(false);
m_bars[i].TextClean();
if(i == index)
continue;
m_bars[i].ValueForAnchoReduce(rest);
const double v = m_bars[i].ValueForAncho();
// Calcular con max_height
const int a = HISTOGRAM_CALCULE_ANCHO(v, m_barras_max_height);
if(a < m_barras_min_width)
{
extra += v;
idx_to_Remove.Push(i);
}
}
//--- Eliminar conjuntos muy peque<EFBFBD>os
const int size_remove = ArraySize(idx_to_Remove);
if(size_remove > 0)
{
for(int i = 0; i < size_remove; i++)
{
const int k = idx_to_Remove[i];
m_bars[k].Destroy();
delete m_bars[k];
}
RemoveMultipleIndexes(m_bars, idx_to_Remove, 0);
m_bars_size -= size_remove;
}
//--- Redistribuir espacio extra
rest = extra / double(m_bars_size);
// Iterar en Y
int y = m_barras_cordinate_init;
// gap_corte es X absoluto
const int gap_corte = m_barras_cordenanda_eje_corte - m_barras_x1;
for(int i = 0; i < m_bars_size; i++)
{
m_bars[i].ValueForAnchoAument(rest);
// Calcular alto con max_height
const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_height);
if(i == index)
{
// Init con par<EFBFBD>metros correctos
m_bars[i].Init(m_function_value_to_pixel,
m_barras_x1, // X fijo
y, // Y variable
gap_corte, // X de corte
a, // Alto
m_factor_conversion_value_to_pixel_pos,
m_factor_conversion_value_to_pixel_neg,
m_fr_change_value);
}
else
{
// SetNewsCordenadasAndSize con par<EFBFBD>metros correctos
m_bars[i].SetNewsCordenadasAndSize(m_barras_x1, y, a);
}
// Avanzar en Y
y += a + m_barras_espacio_entre;
}
}
//+------------------------------------------------------------------+
//| Maneja eliminaci<EFBFBD>n de conjunto (VERTICAL) |
//+------------------------------------------------------------------+
void CHistogramVertical::OnBarrDelete(double extra_value)
{
// La limpieza ya se hace en la clase base
// Distribuir el espacio liberado entre conjuntos restantes
const double rest = extra_value / double(m_bars_size);
// Iterar en Y (vertical), no en X
int y = m_barras_cordinate_init; // Posici<EFBFBD>n Y inicial
// gap_corte es distancia entre x1 y condernada de corte
const int gap_corte = m_barras_cordenanda_eje_corte - m_barras_x1;
for(int i = 0; i < m_bars_size; i++)
{
// Aumentar proporci<EFBFBD>n del conjunto
m_bars[i].ValueForAnchoAument(rest);
// Calcular ALTO (no ancho) usando max_height
const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_height);
// SetNewsCordenadasAndSize con par<EFBFBD>metros correctos
// Para vertical: (x_fijo, y_variable, alto)
m_bars[i].SetNewsCordenadasAndSize(m_barras_x1, y, a);
// Avanzar en Y (no en X)
y += a + m_barras_espacio_entre;
}
}
//+------------------------------------------------------------------+
//| Cambia el ancho/alto de un conjunto (VERTICAL) |
//+------------------------------------------------------------------+
void CHistogramVertical::ConjuntoBarWidth(int index, double new_value)
{
double extra = new_value - m_bars[index].ValueForAncho();
m_bars[index].ValueForAncho(new_value);
if(m_bars_size <= 1)
{
LogWarning("No se puede cambiar ancho con solo 1 grupo", FUNCION_ACTUAL);
return;
}
//--- Si extra > 0: Se aument<EFBFBD> el tama<EFBFBD>o del conjunto 'index'
if(extra > 0.00)
{
double rest = extra / double(m_bars_size - 1);
//--- Reducir proporci<EFBFBD>n de los dem<EFBFBD>s conjuntos
int idx_to_Remove[];
extra = 0.00;
for(int i = 0; i < m_bars_size; i++)
{
m_bars[i].CleanBarras(false);
m_bars[i].TextClean();
if(i != index)
m_bars[i].ValueForAnchoReduce(rest);
const double v = m_bars[i].ValueForAncho();
// Calcular con max_height (vertical)
const int a = HISTOGRAM_CALCULE_ANCHO(v, m_barras_max_height);
if(a < m_barras_min_width)
{
extra += v;
idx_to_Remove.Push(i);
}
}
//--- Eliminar conjuntos muy peque<EFBFBD>os
const int size_remove = ArraySize(idx_to_Remove);
if(size_remove > 0)
{
for(int i = 0; i < size_remove; i++)
{
const int k = idx_to_Remove[i];
m_bars[k].Destroy();
delete m_bars[k];
}
RemoveMultipleIndexes(m_bars, idx_to_Remove, 0);
m_bars_size -= size_remove;
}
//--- Redistribuir espacio extra
rest = extra / double(m_bars_size);
// Iterar en Y (vertical)
int y = m_barras_cordinate_init;
// gap_corte es distancia entre x1 y condernada de corte
const int gap_corte = m_barras_cordenanda_eje_corte - m_barras_x1;
for(int i = 0; i < m_bars_size; i++)
{
m_bars[i].ValueForAnchoAument(rest);
// Calcular alto con max_height
const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_height);
// Par<EFBFBD>metros correctos para vertical
m_bars[i].SetNewsCordenadasAndSize(m_barras_x1, y, a);
// Avanzar en Y
y += a + m_barras_espacio_entre;
}
}
else
{
//--- Si extra < 0: Se redujo el tama<EFBFBD>o del conjunto 'index'
extra *= -1.0;
const double rest = extra / double(m_bars_size - 1);
// Iterar en Y
int y = m_barras_cordinate_init;
// gap_corte es distancia entre x1 y condernada de corte
const int gap_corte = m_barras_cordenanda_eje_corte - m_barras_x1;
//--- Limpiar todos los conjuntos
for(int i = 0; i < m_bars_size; i++)
{
m_bars[i].CleanBarras(false);
m_bars[i].TextClean();
}
//--- Redibujar con nuevas proporciones
for(int i = 0; i < m_bars_size; i++)
{
if(i != index)
m_bars[i].ValueForAnchoAument(rest);
// Calcular alto con max_height
const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_height);
// Par<EFBFBD>metros correctos
m_bars[i].SetNewsCordenadasAndSize(m_barras_x1, y, a);
// Avanzar en Y
y += a + m_barras_espacio_entre;
}
}
}
//+------------------------------------------------------------------+
#undef HISTOGRAM_CALCULE_ANCHO
//+------------------------------------------------------------------+