//+------------------------------------------------------------------+ //| Base.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 #ifndef HISTOGRAM_BARRAS_BASE_BY_LEO_MQH #define HISTOGRAM_BARRAS_BASE_BY_LEO_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "..\\Defines.mqh" #include "..\\Texto.mqh" // Estilo agresivo, no se hacen comprobaciones.. para maxima velocidad.. a tener cuidado con todas las funciones // Notas: // - para horizontal_ m_barras_y1_init nunca es el comienzo se le agrega un gap de 1 para respoectar linea de corte // Clase base para barras normales con fill //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CHistogramConjuntoNor : public CLoggerBase { protected: //--- Barras de un conjutno HistogramBar m_barras[]; // Barras que representan el conjunto ENUM_HIST_DRAW_RECT_STYLE m_barras_style_draw_rect; HistFunctionDrawRect m_function_draw_rect; // Funcion para dibujar las barras int m_barras_size; // Tamaño de barras int m_barras_reserve; // Reserva de barras int m_barras_x1_init; // Y1 inicial para barras int m_barras_y1_init; // X1 inciial para barras int m_barras_gap_eje; // Gap para el eje (solo se usa con corte) bool m_init; //--- Variables cacheadas con el maximo y minimo valor double m_max_value; double m_min_value; //--- Puntero a canvas del CHistogram CCanvasCustom* m_canvas; //--- Funcion para convertir valor a pixel (setean externamente) HistogramFuncionCalculatePixel m_function_calc_pixel; double m_fpos; double m_fneg; double m_fcort; //--- Espacio disponible int m_barras_ancho_total; // Ancho en pixeles sumando todas las barras int m_barras_ancho_unico; // Ancho de cada barra //--- Texto HistogramFuncionCalcTextCordinates m_label_function; HistogramBarLabel m_label; int m_label_gap_by_eje; // Gap del eje //--- Variables controladas externamente no de la clase double m_value_for_ancho; uint m_clean_color; //--- Function que se ejeucta cada vez que se cambia el tamaño de barras (numero de barras) virtual void OnBarrasResize() = 0; // Funcion se que sejeucta cada vez que cambia el max_ancho virtual void ResizeBarra(HistogramBar& barra) = 0; // Funcion para aumentar el "size" virtual void RecalculeBarCordinates() = 0; // Recalcula las cordenadas de las barras virtual void CalculeTextCordinates() = 0; // Calcula las cordenas del texto virtual void OnNewValuesPosNegCorte() = 0; //--- void SetFunctionCalcText(); public: //--- Constructor CHistogramConjuntoNor(); //--- Set general // Esta funcion setea los valores generales de la clase void Set(CCanvasCustom* c, string label, uint txt_clr, ENUM_HISTOGRAM_TEXT_MODE txt_mode, int txt_font_size, string txt_font, int label_gap_eje, ENUM_HIST_DRAW_RECT_STYLE initial_style); // Inicializa la clase void Init(HistogramFuncionCalculatePixel fcalc, int x1_in, int y1_in, int size_corte, int barras_ancho_max, double fpos, double fneg, double fcort); // Inicializa //--- Recalculo //- Funcion que se ejeucta cada vez que se "ropme" un maximo o minimo como para cambiars los "size" de las barras void SetNewsValues(int gap_corte, double fpos, double fneg, double fcort); //- Funcion que se ejecuta cuando se cambia x1, y1, sizebarras void SetNewsCordenadasAndSize(int x1, int y1, int barras_ancho); //- Recalcula las cordenas // Como parametro se le da las cordenadas de un rectnauglo disponible para esta barra // Lo que hace es recaclular las cordendas de los rectangulos y el texto void SetNewCordenadas(int x1, int y1); //- Limpia las barras // clean_color: color con el que se limpiaran las abrras usualente color d efondo // redraw: bandera que indica si se redibujara (si es false solo se camiba los pixeles pero la imagen "externa" no) void CleanBarras(bool free_array, bool redraw = false); //--- Recalculo __forceinline void RecalculeCordinates(); // Recalcula todas las cordenas TEXTO Y BARRAS __forceinline void RecalculeCordinatesOnlyBars(); // Actuliza el ancho y reclaucla cordendas //- Destruccion // Funcion que se llama luego de eliminar el objeto (solo si se limina dinamicamente) void Destroy(); //--- Setter y Getters generales //- Reserve __forceinline int ReserveBarras() const { return m_barras_reserve; } inline void ReserveBarras(int size); //- Tamaño __forceinline int SizeBarras() const { return m_barras_size; } //- Tamaño en pixeles ocupado por todo el conjunto // Getter __forceinline int SizeBarrasInPixels() const { return m_barras_ancho_total;} // Settet void SizeBarrasInPixels(const int new_max_ancho); //- Tamaño en pixeles ocupado por cada barra __forceinline int SizeBarra() const { return m_barras_ancho_unico; } //- Cordenas de inicio __forceinline int XInicial() const { return m_barras_x1_init; } __forceinline int YInicial() const { return m_barras_y1_init; } //- Iniciado ? __forceinline bool IsInit() const { return m_init; } //- Color de limpieza __forceinline uint CleanColor() const { return m_clean_color; } inline void CleanColor(uint new_valuie) { m_clean_color = new_valuie; } //--- Trabajo con barras // update_data, unicamente actuliza pixeles NO la imagen como tal para eso se debera de llamar a canvas.Update() // false: cuando solo se busca añadir barras todavia no hacer el calculo de cordenadas // true: una vez finalizado todo si es que se necesita añadir otra barra extra //- Añadir inline void AddBarraFast(double value, uint clr); void AddBarra(double value, uint clr, bool update_data); //- Remover void RemoveBar(const int index, bool update_data); //- Color inline void BarColor(const int index, const uint new_value); __forceinline uint BarColor(const int index) const { return m_barras[index].clr; } //- Fondo? __forceinline ENUM_HIST_DRAW_RECT_STYLE DrawRectStyle() const { return m_barras_style_draw_rect; } void DrawRectStyle(const ENUM_HIST_DRAW_RECT_STYLE default_style, bool redraw_pixels_in_canvas); void DrawRectStyle(HistFunctionDrawRect funtion_draw, ENUM_HIST_DRAW_RECT_STYLE style_type, bool redraw_pixels_in_canvas); //- Valor // El ultimo parametro update_max_min, se debera poner true luego de llamar inicalemntemente a setmaxmin void BarValue(const int index, const double new_value, bool update_max_min, bool update_rect); __forceinline double BarValue(const int index) const { return m_barras[index].value; } __forceinline double BarMaxValue() const { return m_max_value; } __forceinline double BarMinValue() const { return m_min_value; } //- Maximio y minimo // Recalcual el maixmo y minimo void SetMaxAndMinValue(); //--- Texto del conjunto de barras //- Setter general inline void Text(const string& new_text, uint new_clr); //- Valor __forceinline string TextValue() const { return m_label.value;} inline void TextValue(const string& new_text); //- Color __forceinline uint TextColor() const { return m_label.clr;} inline void TextColor(const uint new_value); inline void TextClean(); // Solo limpia los pixeles y si es necesario redibuja NO ELIMINA EL FONT NI NADA //--- Redibuja void Redraw(); // Redibuja //--- Ancho __forceinline double ValueForAncho() const { return m_value_for_ancho; } inline void ValueForAncho(double v) { m_value_for_ancho = v; } inline void ValueForAnchoReduce(double r) { m_value_for_ancho -= r; } inline void ValueForAnchoAument(double a) { m_value_for_ancho += a; } }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CHistogramConjuntoNor::CHistogramConjuntoNor(void) : m_barras_reserve(0), m_barras_size(0), m_canvas(NULL), m_barras_ancho_total(0), m_barras_ancho_unico(0), m_fcort(0.00), m_fneg(0.00), m_fpos(0.00), m_function_calc_pixel(NULL), m_label_function(NULL), m_barras_x1_init(0), m_barras_y1_init(0), m_init(false), m_barras_gap_eje(0), m_label_gap_by_eje(0) { //--- Valores por defecto para label m_label.clr = clrBlack; m_label.font = "Arial"; m_label.font_size = 5; m_label.mode = WRONG_VALUE; m_label.value = NULL; m_label.x = 0; m_label.y = 0; //--- Resize inicial ArrayResize(m_barras, 0); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramConjuntoNor::Destroy() { //--- if(!m_init) return; //--- Limpiaza su parte en canvas CleanBarras(true); // Limpiamos las barras TextColor(); // Limpaimos el texto } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramConjuntoNor::CleanBarras(bool free_array, bool redraw = false) { for(int i = 0; i < m_barras_size; i++) m_function_draw_rect(m_canvas, m_barras[i].x1, m_barras[i].y1, m_barras[i].x2, m_barras[i].y2, m_clean_color); // Limpiamos el espacio ocupado //--- if(free_array) { ArrayResize(m_barras, 0, m_barras_reserve); m_barras_size = 0; } //--- if(redraw) m_canvas.Update(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramConjuntoNor::ReserveBarras(int size) { m_barras_reserve = size; m_barras.Reserve((uint)size); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline void CHistogramConjuntoNor::AddBarraFast(double value, uint clr) { HistogramBar new_bar(0, 0, 0, 0, value, clr); ArrayResize(m_barras, m_barras_size + 1, m_barras_reserve); m_barras[m_barras_size] = new_bar; m_barras_size++; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramConjuntoNor::AddBarra(double value, uint clr, bool update_data) { HistogramBar new_bar(0, 0, 0, 0, value, clr); ArrayResize(m_barras, m_barras_size + 1, m_barras_reserve); m_barras[m_barras_size] = new_bar; m_barras_size++; //--- Solo recalcular si se pide ademas de que debe de estar iniciado if(m_init && update_data) { //--- Recalculamos el ancho de cada barra HISTOGRAM_CONJ_RECALCULE //--- Recalculamso cordenadas con el nuevo ancho RecalculeCordinatesOnlyBars(); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Esta funcion se ejecuta void CHistogramConjuntoNor::Redraw() { m_canvas.Update(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramConjuntoNor::RemoveBar(const int index, bool update_data) { //--- const int last = m_barras_size - 1; //--- if(m_barras_size > 1) { // Si hay 2 barras o mas enotnces limpiamos la anteiro (si el index es mayor a 0, de lo contiroa limpiamos el siguiente 1) int idx = (index == 0) ? 1 : (index - 1); m_function_draw_rect(m_canvas, m_barras[idx].x1, m_barras[idx].y1, m_barras[idx].x2, m_barras[idx].y2, m_clean_color); } //--- m_function_draw_rect(m_canvas, m_barras[index].x1, m_barras[index].y1, m_barras[index].x2, m_barras[index].y2, m_clean_color); //--- Eliminamos for(int i = index; i < last; i++) m_barras[i] = m_barras[i + 1]; //--- Resize m_barras_size--; ArrayResize(m_barras, m_barras_size, m_barras_reserve); //--- if(update_data) { if(m_barras_size <= 0) { // Ya no hay barras no hace falta recalcular return; } //--- Recalculamos el ancho de cada barra HISTOGRAM_CONJ_RECALCULE //--- Recalculamso cordenadas OnBarrasResize(); // Redibujamos si es necesario } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline void CHistogramConjuntoNor::BarColor(const int index, const uint new_value) { m_barras[index].clr = new_value; m_function_draw_rect(m_canvas, m_barras[index].x1, m_barras[index].y1, m_barras[index].x2, m_barras[index].y2, new_value); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramConjuntoNor::BarValue(const int index, const double new_value, bool update_max_min, bool update_rect) { if(update_max_min) { if(new_value > m_max_value) m_max_value = new_value; if(new_value < m_min_value) m_min_value = new_value; } //--- Limpiamos valor anterior m_function_draw_rect(m_canvas, m_barras[index].x1, m_barras[index].y1, m_barras[index].x2, m_barras[index].y2, m_clean_color); //--- Actulizamos valor m_barras[index].value = new_value; //--- if(update_rect) { ResizeBarra(m_barras[index]); m_function_draw_rect(m_canvas, m_barras[index].x1, m_barras[index].y1, m_barras[index].x2, m_barras[index].y2, m_barras[index].clr); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramConjuntoNor::DrawRectStyle(const ENUM_HIST_DRAW_RECT_STYLE default_style, bool redraw_pixels_in_canvas) { //--- Reseteo m_barras_style_draw_rect = default_style; if(m_barras_style_draw_rect) m_function_draw_rect = HistConjuntoDrawRectFunctions::HistDrawRectFill; else m_function_draw_rect = HistConjuntoDrawRectFunctions::HistDrawRectNoFill; //--- Redibujamos if(redraw_pixels_in_canvas) for(int i = 0; i < m_barras_size; i++) m_function_draw_rect(m_canvas, m_barras[i].x1, m_barras[i].y1, m_barras[i].x2, m_barras[i].y2, m_barras[i].clr); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramConjuntoNor::DrawRectStyle(HistFunctionDrawRect funtion_draw, ENUM_HIST_DRAW_RECT_STYLE style_type, bool redraw_pixels_in_canvas) { //--- Reseteo m_barras_style_draw_rect = style_type; m_function_draw_rect = funtion_draw; //--- Redibujamos if(redraw_pixels_in_canvas) for(int i = 0; i < m_barras_size; i++) m_function_draw_rect(m_canvas, m_barras[i].x1, m_barras[i].y1, m_barras[i].x2, m_barras[i].y2, m_barras[i].clr); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramConjuntoNor::SetMaxAndMinValue() { m_max_value = m_barras[0].value; m_min_value = m_barras[0].value; for(int i = 1; i < m_barras_size; i++) { const double v = m_barras[i].value; if(v > m_max_value) m_max_value = v; if(v < m_min_value) m_min_value = v; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline void CHistogramConjuntoNor::Text(const string &new_text, uint new_clr) { m_label.clr = new_clr; m_label.value = new_text; //--- Cambiamos el texto ::TextSetFont(m_label.font, m_label.font_size); m_canvas.TextOuTF(m_label.x, m_label.y, m_label.value, m_label.clr, m_label.mode); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline void CHistogramConjuntoNor::TextColor(const uint new_value) { m_label.clr = new_value; //--- Cambiamos el texto ::TextSetFont(m_label.font, m_label.font_size); m_canvas.TextOuTF(m_label.x, m_label.y, m_label.value, m_label.clr, m_label.mode); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline void CHistogramConjuntoNor::TextClean() { ::TextSetFont(m_label.font, m_label.font_size); m_canvas.TextOuTF(m_label.x, m_label.y, m_label.value, m_clean_color, m_label.mode); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline void CHistogramConjuntoNor::TextValue(const string &new_text) { //--- ::TextSetFont(m_label.font, m_label.font_size); //--- Limpiamoas el texto anterior m_canvas.TextOuTF(m_label.x, m_label.y, m_label.value, m_clean_color, m_label.mode); //--- Cambiamos el texto m_label.value = new_text; m_canvas.TextOuTF(m_label.x, m_label.y, m_label.value, m_label.clr, m_label.mode); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogramConjuntoNor::SetFunctionCalcText(void) { switch(m_label.mode) { case HISTOGRAM_TEXT_MODE_CENTER: m_label_function = HistogramFuncTxtCalc::HFCTextCenter; break; case HISTOGRAM_TEXT_MODE_LEFT_UPPER: m_label_function = HistogramFuncTxtCalc::HFCTextLeftUpper; break; case HISTOGRAM_TEXT_MODE_RIGHT_UPPER: m_label_function = HistogramFuncTxtCalc::HFCTextRightUpper; break; default: m_label_function = HistogramFuncTxtCalc::HFCTextCenter; break; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ /* Notas: - Niguna de estas fucniones REDIBUJA EL BITMAP, unicamente acutlizan los piexles de canvas, para acutlizar la imagen, lo que ve el usuario Se debera de llamar a Redraw */ //+------------------------------------------------------------------+ void CHistogramConjuntoNor::SetNewCordenadas(int x1, int y1) { //--- Nuevas cordenas de anclaje m_barras_x1_init = x1; m_barras_y1_init = y1; //--- Recalculo de cordenas texto y barras RecalculeCordinates(); } //+------------------------------------------------------------------+ void CHistogramConjuntoNor::SizeBarrasInPixels(const int new_max_ancho) { //--- Vefificacion de tamaño if(m_barras_size < 1 || !m_init) { LogFatalError("No hay barras para el resize, o no esta iniciado", FUNCION_ACTUAL); Remover(); return; } //--- m_barras_ancho_total = new_max_ancho; #ifdef HISTOGRAM_CONJ_ACTIVAR_AUTO_RESIZE //--- Verificacion del texto if(m_label.font_size > m_barras_ancho_total) { LogWarning("Se esta modificando el fontsize, valor mas grande que el ancho disponible", FUNCION_ACTUAL); m_label.font_size = m_barras_ancho_total; } #endif //--- Ancho de la barra HISTOGRAM_CONJ_RECALCULE //--- OnBarrasResize(); // Cambio del "aancho" de las barras, solo el ancho CalculeTextCordinates(); // Recalculo del texto } //+------------------------------------------------------------------+ void CHistogramConjuntoNor::SetNewsCordenadasAndSize(int x1, int y1, int barras_ancho) { //--- Vefificacion de tamaño if(m_barras_size < 1 || !m_init) { LogFatalError("No hay barras para el resize, o no esta iniciado", FUNCION_ACTUAL); Remover(); return; } //--- Nuevas cordenas de anclaje m_barras_x1_init = x1; m_barras_y1_init = y1; //--- Nuevo ancho m_barras_ancho_total = barras_ancho; #ifdef HISTOGRAM_CONJ_ACTIVAR_AUTO_RESIZE //--- Verificacion del texto if(m_label.font_size > m_barras_ancho_total) { LogWarning("Se esta modificando el fontsize, valor mas grande que el ancho disponible", FUNCION_ACTUAL); m_label.font_size = m_barras_ancho_total; } #endif //--- Ancho de la barra HISTOGRAM_CONJ_RECALCULE //---Recalculamos cordenas de texto y barras RecalculeCordinates(); } //+------------------------------------------------------------------+ void CHistogramConjuntoNor::SetNewsValues(int gap_corte, double fpos, double fneg, double fcort) { m_fcort = fcort; m_fneg = fneg; m_fpos = fpos; m_barras_gap_eje = gap_corte; //--- Recalculamos las cordenadas de las barras //RecalculeBarCordinates(); OnNewValuesPosNegCorte(); } //+------------------------------------------------------------------+ // Estra funcion recalcula las cordadenas de todo __forceinline void CHistogramConjuntoNor::RecalculeCordinates(void) { RecalculeBarCordinates(); // Recalculaos y filleamos los recnautrlgos CalculeTextCordinates(); // Recalculamos todas las cordenadas del texto } //+------------------------------------------------------------------+ // Esta funcion se debera de ejeuctar cuando se agergan barras. (luego de m_init = true), y // al momento de finalizar el agregado se debra de ejecutar esto para reclauclos las cordnaas y ancho de cada barra (esto dado qeu se aumentaron barras) __forceinline void CHistogramConjuntoNor::RecalculeCordinatesOnlyBars() { for(int i = 0; i < m_barras_size; i++) m_function_draw_rect(m_canvas, m_barras[i].x1, m_barras[i].y1, m_barras[i].x2, m_barras[i].y2, m_clean_color); // Limpiamos el espacio ocupado (si hjay corndenas no inbiciado no se dibujan) RecalculeBarCordinates(); } //+------------------------------------------------------------------+ //| Seteo general de parametros | //+------------------------------------------------------------------+ /* Advertencia, si el txt_font o txt_font_Size es invalido se toma el valor de canvas (setear el fontsize con FOntSet en CHistogram) */ void CHistogramConjuntoNor::Set(CCanvasCustom *c, string label, uint txt_clr, ENUM_HISTOGRAM_TEXT_MODE txt_mode, int txt_font_size, string txt_font, int label_gap_eje, ENUM_HIST_DRAW_RECT_STYLE initial_style) { //--- General m_canvas = c; //--- m_barras_style_draw_rect = initial_style; if(m_barras_style_draw_rect) // Si no es 0, (fill) m_function_draw_rect = HistConjuntoDrawRectFunctions::HistDrawRectFill; else m_function_draw_rect = HistConjuntoDrawRectFunctions::HistDrawRectNoFill; //--- Texto m_label_gap_by_eje = label_gap_eje; m_label.clr = txt_clr; m_label.mode = txt_mode; m_label.value = label; m_label.font = txt_font == NULL ? c.FontNameGet() : txt_font; m_label.font_size = txt_font_size < 1 ? c.FontSizeGet() : txt_font_size; SetFunctionCalcText(); // Seteamos la funcion para calcular el texto //--- Si ya esta inicado volvermos a recalcular if(m_init) RecalculeCordinates(); // SI se cambio el fill entonces recalcula, si se camibo el texto tambien } //+------------------------------------------------------------------+ //| Inicializacion de la clase | //+------------------------------------------------------------------+ void CHistogramConjuntoNor::Init(HistogramFuncionCalculatePixel fcalc, int x1_in, int y1_in, int size_corte, int barras_ancho_max, double fpos, double fneg, double fcort) { //--- if(m_barras_size < 1) { LogFatalError("No hay barras para crear el conjunto", FUNCION_ACTUAL); Remover(); return; } //--- m_function_calc_pixel = fcalc; //--- General m_init = true; m_barras_x1_init = x1_in; m_barras_y1_init = y1_in; m_fcort = fcort; m_fneg = fneg; m_fpos = fpos; m_barras_gap_eje = size_corte; //--- Ancho de la barra m_barras_ancho_total = barras_ancho_max; HISTOGRAM_CONJ_RECALCULE #ifdef HISTOGRAM_CONJ_ACTIVAR_AUTO_RESIZE //--- Verificacion del texto if(m_label.font_size > m_barras_ancho_total) { LogWarning("Se esta modificando el fontsize, valor mas grande que el ancho disponible", FUNCION_ACTUAL); m_label.font_size = m_barras_ancho_total; } #endif //--- Dibujamos RecalculeCordinates(); } //+------------------------------------------------------------------+ #endif //+------------------------------------------------------------------+