//+------------------------------------------------------------------+ //| Horizontal.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 CHistogramHorizontal : 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: CHistogramHorizontal() { m_type = HISTOGRAM_HORIZONTAL; } 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; 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 }; //+------------------------------------------------------------------+ //| Resize al numero de barras | //+------------------------------------------------------------------+ // Unicamente resize el tamaño de berras // Para poder setear las barras use SetBar con redraw false (todavia no se le asigna cordenadas) // O tambien SetBarFast void CHistogramHorizontal::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 CHistogramConjuntoHorizontal(); m_bars[i].ReserveBarras(reserve_bars); m_bars[i].CleanColor(m_back_color); } m_curr_index_bar_p = 0; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramHorizontal::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) { // 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) return; m_eje = new CHistogramEjeHorizontal(); 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramHorizontal::MinConjuntoAncho(int new_value) { //--- m_barras_min_width = new_value; //--- Reducimos el "factor" de cada uno int idx_to_Remove[]; double extra = 0.00; for(int i = 0; i < m_bars_size; i++) { m_bars[i].CleanBarras(false); // Limpaos las barras (solo pixeles, data no) m_bars[i].TextClean(); // Limpiamos el texto (solo pxiesels, data no) const double v = m_bars[i].ValueForAncho(); //PrintFormat("Ancho para %d = %d", i, m_bars[i].SizeBarrasInPixels()); const int a = HISTOGRAM_CALCULE_ANCHO(v, m_barras_max_width); // PrintFormat("Nuevo ancho para %d = %d", i, a); if(a < m_barras_min_width) // Nuevo ancho para esta bvarra muy pequeño { extra += v; // Sumamos idx_to_Remove.Push(i); } } //--- Eliminamos los muy pequeñ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(); // Limpia el texto ocupado delete m_bars[k]; } RemoveMultipleIndexes(m_bars, idx_to_Remove, 0); m_bars_size -= size_remove; } else { return; // Todos cumplen } //--- const double rest = extra / double(m_bars_size); // si extra es 0.00 esto dara 0.00 (si es 0.00, significa que no se supero el minimo ancho) int x = m_barras_cordinate_init; const int real_y = m_barras_y1 + m_barras_espacio_dibujable_height; // Conseguimos el Y1 real const int gap_corte = real_y - m_barras_cordenanda_eje_corte; // Otenemos el gap de corte respecto a Y1 Real //--- for(int i = 0; i < m_bars_size; i++) { m_bars[i].ValueForAnchoAument(rest); // Aumentamos el ancho const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_width); // Nuevo ancho m_bars[i].SetNewsCordenadasAndSize(x, real_y, a);// Set cordenadas y dibujado pxieles x += a + m_barras_espacio_entre; // Nuewvas cordenadas } } //+------------------------------------------------------------------+ //| Añade un nuevo conjunto de barras | //+------------------------------------------------------------------+ int CHistogramHorizontal::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 CHistogramConjuntoHorizontal(); // 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 CHistogramHorizontal::InitCordinates(void) { int x = m_barras_cordinate_init; const int real_y = m_barras_y1 + m_barras_espacio_dibujable_height; // Conseguimos el Y1 real const int gap_corte = real_y - m_barras_cordenanda_eje_corte; // Otenemos el gap de corte respecto a Y1 Real for(int i = 0; i < m_bars_size; i++) { // Inicilizacion const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_width); m_bars[i].Init(m_function_value_to_pixel, x, real_y, gap_corte, a, m_factor_conversion_value_to_pixel_pos, m_factor_conversion_value_to_pixel_neg, m_fr_change_value); x += a + m_barras_espacio_entre; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramHorizontal::OnSetLienzo(void) { // Setemoas los maximos valores m_barras_max_width = m_barras_espacio_dibujable_width; m_barras_max_width -= (m_bars_size - 1) * m_barras_espacio_entre; // Restamos el el espacio entre barras m_barras_max_width -= (m_barras_gap_init + m_barras_gap_end); // Restamos los dos gaps final e inicial m_barras_max_height = m_barras_espacio_dibujable_height; //--- 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ño const int s = ArraySize(idx_no_tiene); //--- No hay defaults if(s < 1) return; //--- Seteamos los defaults 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 CHistogramHorizontal::RecalculeCordinatesBarsAndText(void) { for(int i = 0; i < m_bars_size; i++) m_bars[i].RecalculeCordinates(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramHorizontal::OnMaxOrMinValueSuperate(void) { //--- Eje si es que existe if(CheckPointer(m_eje)) { m_eje.HistogramLinePointerGet().SetNewValues(m_fr_max_value, m_fr_min_value, m_fr_change_value); } //--- Barras const int real_y = m_barras_y1 + m_barras_espacio_dibujable_height; // Conseguimos el Y1 real const int gap_corte = real_y - m_barras_cordenanda_eje_corte; // Otenemos el gap de corte respecto a Y1 Real 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); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramHorizontal::OnNeweCordinatesBarsAndSize(void) { int x = m_barras_cordinate_init; const int real_y = m_barras_y1 + m_barras_espacio_dibujable_height; // Conseguimos el Y1 real for(int i = 0; i < m_bars_size; i++) { const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_width); m_bars[i].SetNewsCordenadasAndSize(x, real_y, a); x += a + m_barras_espacio_entre; } } //+------------------------------------------------------------------+ //| Funcion que se ejeucta cada vez que se añade o remueve un | //| Eleemto | //+------------------------------------------------------------------+ void CHistogramHorizontal::OnBarraAdd(int index) { //--- m_barras_max_width -= m_barras_espacio_entre; // Espacio //--- double extra = m_bars[index].ValueForAncho(); //--- if(extra == 0.00) // Default { extra = 1.0 / double(m_bars_size); // Valor default } // Print("Nuevo valor: ", extra); double rest = extra / double(m_bars_size - 1); //Print("Se le restara a cada barra: ", rest); //--- Reducimos el "factor" de cada uno int idx_to_Remove[]; extra = 0.00; for(int i = 0; i < m_bars_size; i++) { m_bars[i].CleanBarras(false); // Limpaos las barras (solo pixeles, data no) m_bars[i].TextClean(); // Limpiamos el texto (solo pxiesels, data no) if(i == index) continue; m_bars[i].ValueForAnchoReduce(rest); const double v = m_bars[i].ValueForAncho(); //PrintFormat("Ancho para %d = %d", i, m_bars[i].SizeBarrasInPixels()); const int a = HISTOGRAM_CALCULE_ANCHO(v, m_barras_max_width); // PrintFormat("Nuevo ancho para %d = %d", i, a); if(a < m_barras_min_width) // Nuevo ancho para esta bvarra muy pequeño { extra += v; // Sumamos idx_to_Remove.Push(i); } } //--- Eliminamos los muy pequeñ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(); // Limpia el texto ocupado delete m_bars[k]; } RemoveMultipleIndexes(m_bars, idx_to_Remove, 0); m_bars_size -= size_remove; } //--- rest = extra / double(m_bars_size); // si extra es 0.00 esto dara 0.00 (si es 0.00, significa que no se supero el minimo ancho) int x = m_barras_cordinate_init; const int real_y = m_barras_y1 + m_barras_espacio_dibujable_height; // Conseguimos el Y1 real const int gap_corte = real_y - m_barras_cordenanda_eje_corte; // Otenemos el gap de corte respecto a Y1 Real for(int i = 0; i < m_bars_size; i++) { m_bars[i].ValueForAnchoAument(rest); // Aumentamos el ancho const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_width); if(i == index) // Ultimo eleento { // Inicilizacion m_bars[i].Init(m_function_value_to_pixel, x, real_y, gap_corte, a, m_factor_conversion_value_to_pixel_pos, m_factor_conversion_value_to_pixel_neg, m_fr_change_value); } else { // Reseteo m_bars[i].SetNewsCordenadasAndSize(x, real_y, a);// Set cordenadas } x += a + m_barras_espacio_entre; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramHorizontal::OnBarrDelete(double extra_value) // Elemento ya creado { // La limpeiza ya se hace en la clase base //--- const double rest = extra_value / double(m_bars_size); // (calculamos el valor extra que le sumaremos a los demas indices) int x = m_barras_cordinate_init; const int real_y = m_barras_y1 + m_barras_espacio_dibujable_height; // Conseguimos el Y1 real const int gap_corte = real_y - m_barras_cordenanda_eje_corte; // Otenemos el gap de corte respecto a Y1 Real for(int i = 0; i < m_bars_size; i++) { m_bars[i].ValueForAnchoAument(rest); // Aumentamos el ancho const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_width); //--- Reseteo m_bars[i].SetNewsCordenadasAndSize(x, real_y, a);// Set cordenadas //--- x += a + m_barras_espacio_entre; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramHorizontal::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); // El index ya ocupa todo el espacio del grupo return; } //--- /* if(new_value < 0.0 || new_value > 1.0) { LogError(StringFormat("new_value=%.2f fuera de rango [0.0, 1.0]", new_value), __FUNCTION__); return; } */ //--- if(extra > 0.00) { double rest = extra / double(m_bars_size - 1); //--- Reducimos el "factor" de cada uno int idx_to_Remove[]; extra = 0.00; for(int i = 0; i < m_bars_size; i++) { m_bars[i].CleanBarras(false); // Limpaos las barras (solo pixeles, data no) m_bars[i].TextClean(); // Limpiamos el texto (solo pxiesels, data no) if(i != index) m_bars[i].ValueForAnchoReduce(rest); const double v = m_bars[i].ValueForAncho(); //PrintFormat("Ancho para %d = %d", i, m_bars[i].SizeBarrasInPixels()); const int a = HISTOGRAM_CALCULE_ANCHO(v, m_barras_max_width); // PrintFormat("Nuevo ancho para %d = %d", i, a); if(a < m_barras_min_width) // Nuevo ancho para esta bvarra muy pequeño { extra += v; // Sumamos idx_to_Remove.Push(i); } } //--- Eliminamos los muy pequeñ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(); // Limpia el texto ocupado delete m_bars[k]; } RemoveMultipleIndexes(m_bars, idx_to_Remove, 0); m_bars_size -= size_remove; } //--- rest = extra / double(m_bars_size); // si extra es 0.00 esto dara 0.00 (si es 0.00, significa que no se supero el minimo ancho) int x = m_barras_cordinate_init; const int real_y = m_barras_y1 + m_barras_espacio_dibujable_height; // Conseguimos el Y1 real const int gap_corte = real_y - m_barras_cordenanda_eje_corte; // Otenemos el gap de corte respecto a Y1 Real //--- for(int i = 0; i < m_bars_size; i++) { m_bars[i].ValueForAnchoAument(rest); // Aumentamos el ancho const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_width); // Nuevo ancho m_bars[i].SetNewsCordenadasAndSize(x, real_y, a);// Set cordenadas y dibujado pxieles x += a + m_barras_espacio_entre; // Nuewvas cordenadas } } else { extra *= -1.0; // Lo hacemos positivo const double rest = extra / double(m_bars_size - 1); // (calculamos el valor extra que le sumaremos a los demas indices) int x = m_barras_cordinate_init; const int real_y = m_barras_y1 + m_barras_espacio_dibujable_height; // Conseguimos el Y1 real const int gap_corte = real_y - m_barras_cordenanda_eje_corte; // Otenemos el gap de corte respecto a Y1 Real //--- for(int i = 0; i < m_bars_size; i++) { m_bars[i].CleanBarras(false); m_bars[i].TextClean(); } //--- for(int i = 0; i < m_bars_size; i++) { if(i != index) // Solo aumenta al que no s indice m_bars[i].ValueForAnchoAument(rest); // Aumentamos el ancho //--- const int a = HISTOGRAM_CALCULE_ANCHO(m_bars[i].ValueForAncho(), m_barras_max_width); //--- Reseteo m_bars[i].SetNewsCordenadasAndSize(x, real_y, a);// Set cordenadas y dibujado pxieles //--- x += a + m_barras_espacio_entre; } } } //+------------------------------------------------------------------+ #undef HISTOGRAM_CALCULE_ANCHO //+------------------------------------------------------------------+