//+------------------------------------------------------------------+ //| 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_FINAL_FILE_BASES_BY_LEO_MQH #define HISTOGRAM_FINAL_FILE_BASES_BY_LEO_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "..\\..\\PartInf\\ParteInf.mqh" #include "LineCorte.mqh" #include "..\\Barra\\Barras.mqh" #include "Titulo.mqh" //- Ejes de lineas (Estatico + Secciones) #include "..\\EjeHist\\Ver.mqh" #include "..\\EjeHist\\Hor.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Esta funcion setea los valores del lienzo de dibujo se debera de llamar despues de llenar data y el titulo //--- #define HIST_FLAG_INIT_TITULO 1 #define HIST_FLAG_INIT_DATA 2 #define HIST_FLAG_ALL_INIT (HIST_FLAG_INIT_TITULO | HIST_FLAG_INIT_DATA) #define HIST_FLAG_INIT_CREATE_BITMAP 8 #define HIST_FLAG_INIT_GENERAL 16 #define HIST_FLAG_INIT_PART_INFO 4 //--- #define HISTOGRAM_DEBUG //--- /* Nota para los textos y Font... - El FontSet de canvas sera utiloizado mayormente por el usuario.. por jemeplo si el usuario quiere setear la fuenta debera de usar el fontset de histogram, (hay parametro donde la fuenta es invalida, lo que sifnica que se usara la fuente de canvas, de lo contario una cusotm) */ enum ENUM_HIST_MODE_CORTE { HIST_CORTE_NOT_USE = 0, HIST_CORTE_PERCENT = 1, HIST_CORTE_VALUE = 2 }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CHistogram : public CLoggerBase { protected: CCanvasCustom* m_canvas; // Canvas CColorGeneratorArgb m_color_generator; // Generador de colores aleatorios ENUM_HIST_MODE_CORTE m_corte_mode; //--- int m_bars_size; // Numero de cojuntos de barras CHistogramConjuntoNor* m_bars[]; // Conjunto de barras CHistogramConjuntoNor* m_bar_max; // Barra con el valor maixmo de value CHistogramConjuntoNor* m_bar_min; // Barra con el valor minimo de value //--- uint8_t m_init_flags; // Bandera de incilizacion ENUM_HISTOGAM_TYPE m_type; // Tipo de histograma //--- Generales long m_chart_id; // Id del grafico int m_subwin; // Subventana int m_width; // Ancho total int m_height; // Alto total string m_obj_name; // Nombre del objeto (bitmap o bitmaplabel) uint m_back_color; // Color de fonndo //--- De la imagen //- Conversiones y Corte double m_fr_max_value; // Valor maximo double m_fr_min_value; // Valor minimo double m_fr_change_value; // Valor de cambio double m_factor_conversion_value_to_pixel_pos; // Factor de conversion para valores positivos double m_factor_conversion_value_to_pixel_neg; // Factor de conversion para valores negativos double m_percent_corte_neg; // Porcentaje de corte negativo double m_percent_corte_pos; // Porcentaje de corte positivo HistogramFuncionCalculatePixel m_function_value_to_pixel; // Funcion para convertir valor a pixel CHistogramLineCorteBase* m_line_corte; // Linea de corte //- General // Gaps del linezo de las barras int m_gap_superior; int m_gap_inferior; int m_gap_derecha; int m_gap_izquierda; //- Espacio dibujable de barras int m_barras_gap_init; // Gap inicial (lienzo) int m_barras_gap_end; // Gap final (lienzo) int m_barras_x1; // x1 inicial para barras (del lienzo sin contar gaps) int m_barras_y1; // y1 inicial para barras int m_barras_cordinate_init; // Contando el gap (cordenada de iinciio real) int m_barras_cordenanda_eje_corte; // Cordenanda eje de cambio int m_barras_espacio_dibujable_width; // Ancho disponible para las barras (ejex) int m_barras_espacio_dibujable_height; // Alto disonible para las barras (ejey) int m_barras_max_width; // Maximo ancho total de las barras (sumando todas, sin gaps) int m_barras_max_height; // Maximo alto total de las barras (sumando todas, sin gaps) int m_barras_espacio_entre; // Espacio entre barras int m_barras_min_width; // Minimo ancho //- Titulo CHistogramTitulo* m_titulo; // Titulo //- Parte informativa ENUM_HISTOGRAM_PARTE_INFORMATIVA_POSICION m_part_info_position; CHistogramParteInfo* m_part_info_ptr; //- Copyright CTextCanvas* m_copyright; //- Linea de eje CHistogramEje* m_eje; //--- Añadir valores int m_curr_index_bar_p; // Puntero actual en m_bars //--- Funciones que debera de ser sobreescirtas virtual void RecalculeCordinatesBarsAndText() = 0; // Recalcula las cordenans de todos los conhjntos (barras y texto) virtual void OnNeweCordinatesBarsAndSize() = 0; // Recalcula las cordenans en un nuevo ancho y x1 e y1 () virtual void OnSetLienzo() = 0; // Funcion que se ejeucta una vez seteada los parametros geneles virtual void OnBarraAdd(int index) = 0; // Funcion que se ejeucta cada vez qeu se agrega una nueva barra (ya iniciado) virtual void OnBarrDelete(double extra_value) = 0; // Funcion que se ejeucta cada vez que se elimina una barra (ya iniciado) virtual void OnMaxOrMinValueSuperate() = 0; // Funcion que se ejcuta para reclauclar los "anch9os" una vez superado el miaxmo o minimo virtual void InitCordinates() = 0; // Inicializa todas las clases //--- De la clase void SetMaxMin(); //--- Superacion de maximo o minimo void RecalculeFactorsMaxMinCortePer(); void RecalculeFactorsMaxMinDef(); void RecalculeFactorsMaxMinCorteVal(); void RecalculeFactorsMaxMin(); void OnAfterSetNewMaxMin(); private: void CleanVariables(); public: CHistogram(void); ~CHistogram(void); //--- Creacion // Inicializa la clase void Initialize(int width, int height, string objname, long chart_id, int subwin, uint back_color); // Crea un bitmap inline void CreateBitmap(datetime time, double price, ENUM_COLOR_FORMAT format); // Crea un bitmap lavel inline void CreateBitmapLabel(int x, int y, ENUM_COLOR_FORMAT format); //--- Limpeiza void Clean(); // LIMPIA TODO //--- CColorGeneratorArgb* GetColorGeneratorPointer() { return &m_color_generator; } //--- Trabajo con las Conjuntos de barras //- Numero de barras virtual void NumConjuntoBars(int num_bars, int reserve_bars = 0) = 0; inline int NumConjuntoBars() const { return m_bars_size; } //- Ninimo ancho __forceinline int MinConjuntoAncho() const { return m_barras_min_width; } // Configura un minimo ancho para cada conjunto de barras y hace un resize automativo // Se debera de ejecutar luego de llamar a SetLineizo, si es que sesea un minimo ancho virtual void MinConjuntoAncho(int new_value) = 0; //- Añadir barras // Añade un nuevo conjunto de barras (iniico) // Nota: el "indice de lectura: m_curr_index_bar_p" interno se mueve al ultimo elemento agregado automaticamente, dicho valor se retorna virtual 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.00) = 0 ; // 0.00 = auto (0.00 - 1.00) __forceinline int AddConjuntoBar(const string& label, 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.00); __forceinline int AddConjuntoBarDefault(const string& label, int label_fontsize, int barras_de_reserva_ = 0); void AddConjuntoBarWithBins(const string& label, uint label_clr, int label_fontsize, const string& label_font, ENUM_HISTOGRAM_TEXT_MODE label_mode, int eje_gap, ENUM_HIST_DRAW_RECT_STYLE initial_style, double& data[], int bins, uint clr_barras); __forceinline void AddConjuntoBarWithBinsDeft(const string& label, double& data[], int bins, uint clr_barras = 0); //- Añade una barra a un conjunto __forceinline void AddBarToConjuntoBarFast(double value, uint clr); // Añade una barra al conjunto de barras actual (puntero de lectura) __forceinline void AddBarToConjuntoBar(double value, uint clr, bool update_data); //- Setea un conjunto de barras (util si se predinifo el numero de cokunto con NumConjuntoBars) // Dado que dicha funcion no lo setea, asi que debra de usar esta funcion // Se debea de configurar el indice de lectura m_curr_index_bar_p __forceinline void BarConjuntoSet(const string label, uint label_clr, int label_fontsize, const string &label_font, ENUM_HISTOGRAM_TEXT_MODE label_mode, int eje_gap, ENUM_HIST_DRAW_RECT_STYLE initial_style, double width = 0.00); __forceinline void BarConjuntoSet(const string label, int label_fontsize, const string &label_font, ENUM_HISTOGRAM_TEXT_MODE label_mode, int eje_gap, ENUM_HIST_DRAW_RECT_STYLE initial_style, double width = 0.00); // Color aleatorio //- Setea la data de un conjunto por lote () // Requieuire indice void BarConjuntoSetBath(int index, const double& values[], const uint& colores[], bool redraw = false); // utiliza el indice interno __forceinline void BarConjuntoSetBathCurrReadIndex(const double& values[], const uint& colores[], bool redraw = false) { BarConjuntoSetBath(m_curr_index_bar_p, values, colores, redraw);} //- Eliminacion // index = indice del conjunto de barras (m_bars) aquyi no se usa el putneor de lecutra (indice de lectura) void RemoveConjuntoBar(int index, bool redraw = false); __forceinline void RemoveConjuntoBar(string name, bool redraw = false); __forceinline void RemoveConjuntoBarCurrReadIndex(bool redraw = false) { RemoveConjuntoBar(m_curr_index_bar_p, redraw); } // Utiliza el punteor de lecutra interno por defecto //- Cambia el nombre de un conjunto __forceinline void BarConjuntoTextValue(string new_name, bool redraw = false); // Requiere tenmer establecido el indice de lecutra __forceinline string BarConjuntoTextValue(int index) const { return m_bars[index].TextValue(); } // Requiere indice no el punteor de lectura __forceinline string BarConjuntoTextValueCurrReadIndex() const { return m_bars[m_curr_index_bar_p].TextValue(); } // No requiere nada, se accede atravez del indnice de lectura //- Ancho del conjunto virtual void ConjuntoBarWidth(int index, double new_value) = 0; __forceinline double ConjuntoBarWidth(int index) const { return m_bars[index].ValueForAncho(); } //Requiere indice de lectura __forceinline double ConjuntoBarWidthCurrReadIndex() const { return m_bars[m_curr_index_bar_p].ValueForAncho(); } // No requiere, auto, se utiliza el dincei por defcveto //- Estilo de dibujado __forceinline void DrawRectStyleConjuntoBar(int index, ENUM_HIST_DRAW_RECT_STYLE style, bool update_pixles) { m_bars[index].DrawRectStyle(style, update_pixles); } __forceinline void DrawRectStyleConjuntoBarReadIndex(ENUM_HIST_DRAW_RECT_STYLE style, bool update_pixles) { m_bars[m_curr_index_bar_p].DrawRectStyle(style, update_pixles); } __forceinline void DrawRectStyleConjuntoBar(int index, HistFunctionDrawRect function, ENUM_HIST_DRAW_RECT_STYLE style, bool update_pixles) { m_bars[index].DrawRectStyle(function, style, update_pixles); } __forceinline void DrawRectStyleConjuntoBarReadIndex(HistFunctionDrawRect function, ENUM_HIST_DRAW_RECT_STYLE style, bool update_pixles) { m_bars[m_curr_index_bar_p].DrawRectStyle(function, style, update_pixles); } __forceinline ENUM_HIST_DRAW_RECT_STYLE DrawRectStyleConjuntoBar(int index) const { return m_bars[index].DrawRectStyle(); } __forceinline ENUM_HIST_DRAW_RECT_STYLE DrawRectStyleConjuntoBarReadIndex() const { return m_bars[m_curr_index_bar_p].DrawRectStyle(); } //- Finaliza el add de barras a un conjunot (no es necesario si m_init es false, de lo contrario SI) void FinalizeAddConjunto(int index_fin, bool update_data, bool redraw = false); //- Encontrar y retornar inline int FindIndexByBarConjuntoName(const string& name); //--- Puntero de lectura // Funciones para cambiar el puntero de lectura interno (indice de lectura (conjunto bars) de m_bars) // Obtiene el puntero/indice de lectura de m_bars __forceinline int ReadIndex() const { return (m_curr_index_bar_p); } __forceinline int ReadIndex(const int conjunto_bar_index) { return (m_curr_index_bar_p = conjunto_bar_index); } __forceinline int ReadIndex(const string& name) { return (m_curr_index_bar_p = FindIndexByBarConjuntoName(name)); } __forceinline int ReadIndexFisrtConjunto() { return (m_curr_index_bar_p = 0); } __forceinline int ReadIndexLastConjunto() { return (m_curr_index_bar_p = m_bars_size - 1); } __forceinline int ReadIndexAument(int add_value = 1) { return (m_curr_index_bar_p += add_value); } __forceinline int ReadIndexReduce(int reduce_value = 1) { return (m_curr_index_bar_p -= reduce_value); } __forceinline int ReadIndexMiddle() { return (m_curr_index_bar_p = int(round(m_bars_size / 2))); } //--- Trabajo con una barra espcifica de un conjunto de barras // index: indice en el conjunto de barras NO de m_bars (este se setea con el indice interno) // Setea el color de una barra __forceinline void BarColor(int index, uint new_clr, bool redraw = false); __forceinline uint BarColor(int index) const { return m_bars[m_curr_index_bar_p].BarColor(index); } // Setea el valor de una barra inline void BarValue(int index, double new_value, bool redraw = false); __forceinline double BarValue(int index) const { return m_bars[m_curr_index_bar_p].BarValue(index); } // Elimina una barra de un conjunto de barras __forceinline void RemoveBarFromConjuntoBar(int index_in_conjunto, bool redraw = false); // Tamaño __forceinline int SizeBarsOfConjunto() const { return m_bars[m_curr_index_bar_p].SizeBarras(); } //--- FontSet __forceinline void FontSet(const string name, const int size, const uint flags, const uint angle) { m_canvas.FontSet(name, size, flags, angle);} //--- Titulo void Titulo(const string &titulo, ENUM_HISTOGRAM_TITULO_POSICION posicion, int y_gap_titulo, uint aligement, int x, int y, uint clr, int fontsize = 0, string font = NULL, uint flagtext = UINT_MAX, uint textpos = UINT_MAX); void TituloDefault(const string& titulo, int espacio_utilizado_por_el_titulo_y = 30); CHistogramTitulo* TituloGetPointer() { return m_titulo; } //--- Copyright CTextCanvas* CopyrightGetPointer() { return m_copyright;} void CreateCopyright(int x, int y, string txt, uint clr, uint align, int fontsize = 0, string font = NULL, uint flagtext = UINT_MAX, uint textpos = UINT_MAX); void CreateCopyrightDefault(const string& autor, string font = "Arial", int fontsize = 15); //--- Linea de eje y Estatica virtual 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) = 0; CHistogramEje* EjeLineGetPointer() { return m_eje;} //--- Lienzo general // Inciiliza el liezo sin valores de corte (defualt) void InitLienzoBarras(int x1, int y1, int x2, int y2, int gap_init, int gap_end, int gap_espacio_entre_barras); // Inciliza el lienzo con valores de corte (porcentajes) void InitLienzoBarras(int x1, int y1, int x2, int y2, int gap_init, int gap_end, int gap_espacio_entre_barras, double value_corte_neg, double value_corte_pos); // Inciiliza el liezo sin valores de corte (valor) void InitLienzoBarras(int x1, int y1, int x2, int y2, int gap_init, int gap_end, int gap_espacio_entre_barras, double value_corte); //--- Parte informativa - legenda void CreatePartInformativa(ENUM_HISTOGRAM_PARTE_INFORMATIVA_POSICION pos, int xgap_inicial, int ygap_inicial); __forceinline CHistogramParteInfo* GetPartInfPointer() { return m_part_info_ptr; } //--- Linea de corte CHistogramLineCorteBase* GetLineCortePtr() { return m_line_corte; } // Solo existe si es que hay corte si no no //--- Redibujado __forceinline void Redraw(); //--- Guardar como imagen bool SavePicture(const string& filename, bool comon); // Guarda la imagen //--- Obteener array de pixeles void GetPixelsArray(uint& out_array_px[]) { m_canvas.GetPixelsArray(out_array_px); } //--- Generales __forceinline long ChartId() const { return m_chart_id; } __forceinline int Subwin() const { return m_subwin; } __forceinline string ObjName() const { return m_obj_name; } __forceinline int Width() const { return m_width; } __forceinline int Height() const { return m_height; } __forceinline uint BackColor() const { return m_back_color; } //--- Inicilizacion __forceinline bool IsInitTitulo() const { return (m_init_flags & HIST_FLAG_INIT_TITULO) != 0; } __forceinline bool IsInitBarras() const { return (m_init_flags & HIST_FLAG_INIT_DATA) != 0; } __forceinline bool IsInitPartInf() const { return (m_init_flags & HIST_FLAG_INIT_PART_INFO) != 0; } __forceinline bool IsInitGeneral() const { return (m_init_flags & HIST_FLAG_INIT_GENERAL) != 0; } __forceinline bool IsCreateObj() const { return (m_init_flags & HIST_FLAG_INIT_CREATE_BITMAP) != 0; } }; //+------------------------------------------------------------------+ //| Macros | //+------------------------------------------------------------------+ //--- #define HISTOGRAM_VALUE_TO_PIXEL(value) m_function_value_to_pixel(value, m_fr_change_value, m_factor_conversion_value_to_pixel_pos, m_factor_conversion_value_to_pixel_neg) //--- #define HISTOGRAM_IS_INIT_TITULO ((m_init_flags & HIST_FLAG_INIT_TITULO) != 0) #define HISTOGRAM_IS_INIT_DATA ((m_init_flags & HIST_FLAG_INIT_DATA) != 0) #define HISTOGRAM_IS_INIT_INFORMATIVE ((m_init_flags & HIST_FLAG_INIT_PART_INFO) != 0) #define HISTOGRAM_IS_INIT_CREATE ((m_init_flags & HIST_FLAG_INIT_CREATE_BITMAP) != 0) #define HISTOGRAM_IS_INIT_GENERAL ((m_init_flags & HIST_FLAG_INIT_GENERAL) != 0) // nes = solo lo necesario, informativo es extra no hace falta #define HISTOGRAM_IS_INIT_NES ((m_init_flags & HIST_FLAG_ALL_INIT) == HIST_FLAG_ALL_INIT) //--- #define HISTOGRAM_IS_NOT_INIT_TITULO ((m_init_flags & HIST_FLAG_INIT_TITULO) == 0) #define HISTOGRAM_IS_NOT_INIT_DATA ((m_init_flags & HIST_FLAG_INIT_DATA) == 0) #define HISTOGRAM_IS_NOT_INIT_INFORMATIVE ((m_init_flags & HIST_FLAG_INIT_PART_INFO) == 0) //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CHistogram::CHistogram(void) { CleanVariables(); m_canvas = new CCanvasCustom(); m_canvas.FontSet("Arial", 12, 0, 0); // Seteamos un font por defecto } //+------------------------------------------------------------------+ CHistogram::~CHistogram() { Clean(); } //+------------------------------------------------------------------+ //| Limpieza de variables | //+------------------------------------------------------------------+ void CHistogram::CleanVariables(void) { //--- m_canvas = NULL; m_titulo = NULL; m_copyright = NULL; m_eje = NULL; m_corte_mode = WRONG_VALUE; //--- Barras m_bars_size = 0; m_bar_max = NULL; m_bar_min = NULL; m_curr_index_bar_p = 0; ArrayResize(m_bars, 0); //--- Flags y tipo m_init_flags = 0; m_type = WRONG_VALUE; //--- Generales m_chart_id = 0; m_subwin = 0; m_width = 0; m_height = 0; m_obj_name = ""; m_back_color = 0x00000000; //--- Conversiones y corte m_fr_max_value = 0.0; m_fr_min_value = 0.0; m_fr_change_value = 0.0; m_factor_conversion_value_to_pixel_pos = 0.0; m_factor_conversion_value_to_pixel_neg = 0.0; m_percent_corte_neg = 0.0; m_percent_corte_pos = 0.0; m_function_value_to_pixel = NULL; m_line_corte = NULL; //--- Gaps m_gap_superior = 0; m_gap_inferior = 0; m_gap_derecha = 0; m_gap_izquierda = 0; //--- Espacio dibujable de barras m_barras_gap_init = 0; m_barras_gap_end = 0; m_barras_x1 = 0; m_barras_y1 = 0; m_barras_cordinate_init = 0; m_barras_cordenanda_eje_corte = 0; m_barras_espacio_dibujable_width = 0; m_barras_espacio_dibujable_height = 0; m_barras_max_width = 0; m_barras_max_height = 0; m_barras_espacio_entre = 0; m_barras_min_width = 0; //--- Parte informativa m_part_info_position = WRONG_VALUE; m_part_info_ptr = NULL; } //+------------------------------------------------------------------+ //| Limpieza total | //+------------------------------------------------------------------+ void CHistogram::Clean(void) { //--- Eliminamos ptrs // Parte informativa if(CheckPointer(m_part_info_ptr) == POINTER_DYNAMIC) delete m_part_info_ptr; // Barras for(int i = 0; i < m_bars_size; i++) { if(CheckPointer(m_bars[i]) == POINTER_DYNAMIC) delete m_bars[i]; } // Linea de corte if(CheckPointer(m_line_corte) == POINTER_DYNAMIC) delete m_line_corte; // Canvas if(CheckPointer(m_canvas) == POINTER_DYNAMIC) { m_canvas.Destroy(); // Eliminamicino del obejto y liberacion del recurso dinamico delete m_canvas; } // Titulo if(CheckPointer(m_titulo) == POINTER_DYNAMIC) delete m_titulo; // Copyright if(CheckPointer(m_copyright) == POINTER_DYNAMIC) { delete m_copyright; m_copyright = NULL; } // Linea de eje if(CheckPointer(m_eje) == POINTER_DYNAMIC) { delete m_eje; m_eje = NULL; } //--- CleanVariables(); // limpieza de variables } //+------------------------------------------------------------------+ //| Inicializa la clase | //+------------------------------------------------------------------+ void CHistogram::Initialize(int width, int height, string objname, long chart_id, int subwin, uint back_color) { //--- if(HISTOGRAM_IS_INIT_GENERAL) return; // Si ya se seteo entonces retornar //--- m_init_flags |= HIST_FLAG_INIT_GENERAL; //--- m_width = width; m_height = height; m_obj_name = objname; m_subwin = subwin; m_chart_id = chart_id; m_back_color = back_color; } //+------------------------------------------------------------------+ //| Crea un bitmap | //+------------------------------------------------------------------+ inline void CHistogram::CreateBitmap(datetime time, double price, ENUM_COLOR_FORMAT format) { if(HISTOGRAM_IS_INIT_CREATE) return; // Si el objeto ya esta creado salir m_init_flags |= HIST_FLAG_INIT_CREATE_BITMAP; m_canvas.CreateBitmap(m_chart_id, m_subwin, m_obj_name, time, price, m_width, m_height, format); m_canvas.Erase(m_back_color); } //+------------------------------------------------------------------+ //| Crea un bitmap label | //+------------------------------------------------------------------+ inline void CHistogram::CreateBitmapLabel(int x, int y, ENUM_COLOR_FORMAT format) { if(HISTOGRAM_IS_INIT_CREATE) return; // Si el objeto ya esta creado salir m_init_flags |= HIST_FLAG_INIT_CREATE_BITMAP; m_canvas.CreateBitmapLabel(m_chart_id, m_subwin, m_obj_name, x, y, m_width, m_height, format); m_canvas.Erase(m_back_color); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogram::AddConjuntoBarWithBins(const string &label, uint label_clr, int label_fontsize, const string &label_font, ENUM_HISTOGRAM_TEXT_MODE label_mode, int eje_gap, ENUM_HIST_DRAW_RECT_STYLE initial_style, double &data[], int bins, uint clr_barras) { //--- const int idx_conj = AddConjuntoBar(label, label_clr, label_fontsize, label_font, label_mode, bins, eje_gap, initial_style); //--- const int data_size = ArraySize(data); double min = data[0]; double max = data[0]; for(int i = 1; i < data_size; i++) { const double v = data[i]; if(v > max) max = v; if(v < min) min = v; } //--- double bins_range = (max - min) / double(data_size); //--- int frecuencias_data[]; ArrayResize(frecuencias_data, bins); //--- for(int i = 0; i < data_size; i++) { int idx = int((data[i] - min) / bins_range); if(idx >= bins) idx = bins - 1; frecuencias_data[idx]++; } //--- for(int i = 0; i < bins; i++) { const double v = (double)frecuencias_data[i]; AddBarToConjuntoBarFast(v, clr_barras); } //--- FinalizeAddConjunto(idx_conj, true, false); // NO redibuja } //+------------------------------------------------------------------+ __forceinline void CHistogram::AddConjuntoBarWithBinsDeft(const string &label, double &data[], int bins, uint clr_barras = 0) { AddConjuntoBarWithBins(label, ColorToARGB(clrBlack), 12, "Arial", HISTOGRAM_TEXT_MODE_CENTER, 30, HIST_DRAW_RECT_FILL, data, bins, (clr_barras == 0 ? m_color_generator.Next() : clr_barras)); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ __forceinline int CHistogram::AddConjuntoBar(const string &label, 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) { return AddConjuntoBar(label, m_color_generator.Next(), label_fontsize, label_font, label_mode, barras_de_reserva_, eje_gap, initial_style, width); } //+------------------------------------------------------------------+ __forceinline int CHistogram::AddConjuntoBarDefault(const string &label, int label_fontsize, int barras_de_reserva_ = 0) { return AddConjuntoBar(label, m_color_generator.Next(), label_fontsize, "Arial", HISTOGRAM_TEXT_MODE_CENTER, barras_de_reserva_, label_fontsize >> 1, HIST_DRAW_RECT_FILL); } //+------------------------------------------------------------------+ //| Setea un conjunto de barras | //+------------------------------------------------------------------+ __forceinline void CHistogram::BarConjuntoSet(const string label, uint label_clr, int label_fontsize, const string &label_font, ENUM_HISTOGRAM_TEXT_MODE label_mode, int eje_gap, ENUM_HIST_DRAW_RECT_STYLE initial_style, double width = 0.00) { m_bars[m_curr_index_bar_p].Set(m_canvas, label, label_clr, label_mode, label_fontsize, label_font, eje_gap, initial_style); m_bars[m_curr_index_bar_p].ValueForAncho(width); } //+------------------------------------------------------------------+ __forceinline void CHistogram::BarConjuntoSet(const string label, int label_fontsize, const string &label_font, ENUM_HISTOGRAM_TEXT_MODE label_mode, int eje_gap, ENUM_HIST_DRAW_RECT_STYLE initial_style, double width = 0.00) { m_bars[m_curr_index_bar_p].Set(m_canvas, label, m_color_generator.Next(), label_mode, label_fontsize, label_font, eje_gap, initial_style); m_bars[m_curr_index_bar_p].ValueForAncho(width); } //+------------------------------------------------------------------+ //| Añade una barra al conjunto de barras | //+------------------------------------------------------------------+ // Rapida __forceinline void CHistogram::AddBarToConjuntoBarFast(double value, uint clr) { m_bars[m_curr_index_bar_p].AddBarraFast(value, clr); } //+------------------------------------------------------------------+ // Lenta __forceinline void CHistogram::AddBarToConjuntoBar(double value, uint clr, bool update_data) { m_bars[m_curr_index_bar_p].AddBarra(value, clr, update_data); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogram::BarConjuntoSetBath(int index, const double &values[], const uint &colores[], bool redraw = false) { const int s_v = ArraySize(values); const int s_c = ArraySize(colores); //--- Comparacion if(s_v != s_c) { LogCriticalError("Array de valores y colores, difieren en tamaño", FUNCION_ACTUAL); Remover(); return; } //--- Agregamos for(int i = 0; i < s_v; i++) m_bars[index].AddBarraFast(values[i], colores[i]); //---Finalizamos FinalizeAddConjunto(index, true, redraw); // SOlo se ejeucta cuando se haya completado las banderas necesarias } //+------------------------------------------------------------------+ //| Finaliza el agregado de barras de un conjunto | //| El color de la label se elije aleatoriamente | //+------------------------------------------------------------------+ void CHistogram::FinalizeAddConjunto(int index_fin, bool update_data, bool redraw = false) { if(HISTOGRAM_IS_INIT_NES) // Si ya esta iniciado ya se calculo, entonces necesitamos recalcular { const bool ya_existe = m_bars[index_fin].IsInit(); //--- Set max min m_bars[index_fin].SetMaxAndMinValue(); //--- Check rango if(m_bars[index_fin].BarMaxValue() > m_bar_max.BarMaxValue()) { m_bar_max = m_bars[index_fin]; //--- RecalculeFactorsMaxMin(); OnMaxOrMinValueSuperate(); // Recalculamos "los valores" de las barras if(m_corte_mode) // No es 0 m_line_corte.SetNewValueEjePrincipal(m_barras_cordenanda_eje_corte, false); } else if(m_bars[index_fin].BarMinValue() < m_bar_min.BarMinValue()) { m_bar_min = m_bars[index_fin]; //--- RecalculeFactorsMaxMin(); OnMaxOrMinValueSuperate(); // Recalculamos "los valores" de las barras if(m_corte_mode) // No es 0 m_line_corte.SetNewValueEjePrincipal(m_barras_cordenanda_eje_corte, false); } //--- if(ya_existe) { // Calculamos las cordenadas if(update_data) m_bars[index_fin].RecalculeCordinatesOnlyBars(); } else { OnBarraAdd(index_fin); // Se actulizan los tamaños de las barras, se reclacula el ancho de cada conjunto } //--- if(redraw) // Redibujamos m_canvas.Update(); } #ifdef HISTOGRAM_DEBUG else { LogWarning("Solo se puede ejecutar FinalizeAddConjunto, para agregar barras a un histograma ya creado", FUNCION_ACTUAL); } #endif } //+------------------------------------------------------------------+ //| Elimina una conjunto de barras por indice | //+------------------------------------------------------------------+ void CHistogram::RemoveConjuntoBar(int index, bool redraw) { // Nota: // - No ejeuctar cuando no hay elemetos // - index debera de ser valido // - Creo que podriamos acutlizar max y min con bucle pero no es necesario.. talvez a futuro //--- double v = m_bars[index].ValueForAncho(); //--- if(m_bars_size > 1) { int idx = (index == 0) ? 1 : (index - 1); m_bars[idx].CleanBarras(false, false); } //--- Elimianmos ptr m_bars[index].Destroy(); // Limpiamos el espacio ocupado (texto y barras) delete m_bars[index]; //--- Removemos for(int i = index; i < m_bars_size - 1; i++) { m_bars[i] = m_bars[i + 1]; } //--- m_bars_size--; ArrayResize(m_bars, m_bars_size); //--- if(m_bars_size > 0) // Solo revisar si al menos hay 1 barra, si no hay nada no hay que revisar OnBarrDelete(v); //--- SI se peude redibujar recalculamos las cordendas if(redraw) { m_canvas.Update(); } } //+------------------------------------------------------------------+ //| Elimina una barra por nombre | //+------------------------------------------------------------------+ __forceinline void CHistogram::RemoveConjuntoBar(string name, bool redraw) { RemoveConjuntoBar(FindIndexByBarConjuntoName(name), redraw); } //+------------------------------------------------------------------+ //| Elimina una barra de su conjunto | //+------------------------------------------------------------------+ __forceinline void CHistogram::RemoveBarFromConjuntoBar(int index_in_conjunto, bool redraw = false) { m_bars[m_curr_index_bar_p].RemoveBar(index_in_conjunto, true); if(redraw) m_canvas.Update(); } //+------------------------------------------------------------------+ //| Establece el color de una barra por indice | //+------------------------------------------------------------------+ __forceinline void CHistogram::BarColor(int index, uint new_clr, bool redraw = false) { m_bars[m_curr_index_bar_p].BarColor(index, new_clr); if(redraw) m_canvas.Update(); } //+------------------------------------------------------------------+ //| Establece el valor de una barra por indice | //+------------------------------------------------------------------+ void CHistogram::BarValue(int index, double new_value, bool redraw = false) { // Notas: // - Solo ejecutar luego de haber iniciado la clase // - m_curr_index_bar_p y index, no se compruebam, debera de ser validos //--- Chekeamos valores if(new_value > m_bar_max.BarMaxValue()) { m_bars[m_curr_index_bar_p].BarValue(index, new_value, true, false); // Actulizamos (no el rect se hara en OnMar...) m_bar_max = m_bars[m_curr_index_bar_p]; RecalculeFactorsMaxMin(); OnMaxOrMinValueSuperate(); // Recalculamos "los valores" de las barras if(m_corte_mode) // No es 0 m_line_corte.SetNewValueEjePrincipal(m_barras_cordenanda_eje_corte, false); } else if(new_value < m_bar_min.BarMinValue()) { m_bars[m_curr_index_bar_p].BarValue(index, new_value, true, false); // Actulizamos (no el rect se hara en OnMar...) m_bar_min = m_bars[m_curr_index_bar_p]; RecalculeFactorsMaxMin(); OnMaxOrMinValueSuperate(); // Recalculamos "los valores" de las barras if(m_corte_mode) // No es 0 m_line_corte.SetNewValueEjePrincipal(m_barras_cordenanda_eje_corte, false); } else // No es un pico, actulizamos el rectangulo { m_bars[m_curr_index_bar_p].BarValue(index, new_value, true, true); // Actulizamos } //--- if(redraw) m_canvas.Update(); } //+------------------------------------------------------------------+ //| Establece el color de una barra por indice | //+------------------------------------------------------------------+ __forceinline void CHistogram::BarConjuntoTextValue(string new_name, bool redraw = false) { m_bars[m_curr_index_bar_p].TextValue(new_name); if(redraw) m_canvas.Update(); } //+------------------------------------------------------------------+ //| Encuentra el indice de un conjunto de barras por su nombres | //+------------------------------------------------------------------+ inline int CHistogram::FindIndexByBarConjuntoName(const string& name) { for(int i = 0; i < m_bars_size; i++) if(m_bars[i].TextValue() == name) return i; return -1; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogram::Titulo(const string &titulo, ENUM_HISTOGRAM_TITULO_POSICION posicion, int y_gap_titulo, uint aligement, int x, int y, uint clr, int fontsize = 0, string font = NULL, uint flagtext = UINT_MAX, uint textpos = UINT_MAX) { //--- if(HISTOGRAM_IS_INIT_TITULO) return; m_titulo = new CHistogramTitulo(); m_titulo.Create(m_canvas, x, y, titulo, clr, aligement, posicion, y_gap_titulo, fontsize, font, flagtext, textpos); m_titulo.CleanColor(m_back_color); m_init_flags |= HIST_FLAG_INIT_TITULO; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogram::TituloDefault(const string &titulo, int espacio_utilizado_por_el_titulo_y = 30) { int fontsize = (espacio_utilizado_por_el_titulo_y >> 1) - 3; int x_mitad = m_width >> 1; int y_mitad = espacio_utilizado_por_el_titulo_y >> 1; Titulo(titulo, HISTOGRAM_TITULO_ARRIBA, espacio_utilizado_por_el_titulo_y, TA_CENTER | TA_VCENTER, x_mitad, y_mitad, ColorToARGB(clrBlack), fontsize, "Arial", 0); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogram::SetMaxMin(void) { m_bars[0].SetMaxAndMinValue(); m_bar_max = m_bars[0]; m_bar_min = m_bars[0]; for(int i = 1; i < m_bars_size; i++) { m_bars[i].SetMaxAndMinValue(); if(m_bars[i].BarMaxValue() > m_bar_max.BarMaxValue()) m_bar_max = m_bars[i]; if(m_bars[i].BarMinValue() < m_bar_min.BarMinValue()) m_bar_min = m_bars[i]; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogram::InitLienzoBarras(int x1, int y1, int x2, int y2, int gap_init, int gap_end, int gap_espacio_entre_barras) { //--- Check init /* - Para este caso damos la posiblidad que se ejcute denuevo init - Se debera de tenrer cuidad con x1, y2, etc para que "colisionen" con el texto informativo creado if(HISTOGRAM_IS_INIT_ALL) { LogError(") return; } */ //--- Chek titulo if(HISTOGRAM_IS_NOT_INIT_TITULO) { LogError("Primero se tuvo que haber seteado el titulo", FUNCION_ACTUAL); return; } //--- Check Y // Nota: El titulo siempre se setea antes de el lienzo siempre if(m_titulo.TituloPosition() == HISTOGRAM_TITULO_ARRIBA) { if(y1 < m_titulo.ReserveY()) // El y1 inicia antes que la altura reservada para el titulo, conflicvto { LogError("Y1 Del titulo inicia antes de la altura reservada para el titulo", FUNCION_ACTUAL); return; } if(y2 > m_height) // FUERA DE RANGHO { LogError(StringFormat("Y2[%d] Final mayor a height[%d]", y2, m_height), FUNCION_ACTUAL); return; } // A que tener en cuenta que el y2 tambien debe de considerar el espacio para los texto fontsize que se puso al moet o de agregar barras // Como es variable no podemos comporarlo // En teoria podriamos metere mas comprobaciones para todos los parametros pero creo que no hace falta } else if(m_titulo.TituloPosition() == HISTOGRAM_TITULO_ABAJO) { const int y2_titulo = m_width - m_titulo.ReserveY(); if(y2 > y2_titulo) // el y2 (eje y de fin) esta mas adelnta que el y2 del titulo, conflicto { LogError("Y2 Del linezo mayor al espacio inferior reservado para el titulo", FUNCION_ACTUAL); return; } if(y1 < 1) { LogError("Y1 Negativo", FUNCION_ACTUAL); return; } // A que tener en cuenta que el y2 tambien debe de considerar el espacio para los texto fontsize que se puso al moet o de agregar barras // Como es variable no podemos comporarlo } //--- m_corte_mode = HIST_CORTE_NOT_USE; //--- m_init_flags |= HIST_FLAG_INIT_DATA; //--- m_barras_gap_end = gap_end; m_barras_gap_init = gap_init; m_barras_x1 = x1; m_barras_y1 = y1; m_barras_espacio_dibujable_height = fabs(y2 - y1); m_barras_espacio_dibujable_width = fabs(x2 - x1); m_barras_espacio_entre = gap_espacio_entre_barras; //--- const bool is_h = (m_type == HISTOGRAM_HORIZONTAL); //--- Cordenajda de inicial real if(is_h) m_barras_cordinate_init = m_barras_x1 + m_barras_gap_init; else m_barras_cordinate_init = m_barras_y1 + m_barras_gap_init; //--- m_gap_izquierda = x1; // Gap entre el ejey y el "left" de la iamge m_gap_derecha = m_width - (m_gap_izquierda + m_barras_espacio_dibujable_width); // Calculamos el gap de la derecha (faltante) m_gap_inferior = m_height - y2; // Gap entre el ejex (inf) y el "bottom" de la imagen m_gap_superior = y1; //--- OnSetLienzo(); //--- Steamos maximo y minimo SetMaxMin(); // Este gap es para cubrir el espacio de la liena de crote (para no suporniciones colisiones de pixeles) // 1 De la linea de corte // 1 liena del eje #define HISTOGRAM_GAP_LINE_CORTE 2 //--- Maximo y minimo m_fr_max_value = m_bar_max.BarMaxValue(); m_fr_min_value = m_bar_min.BarMinValue(); //--- Ptp const double ptp = m_fr_max_value - m_fr_min_value; double fc; //--- Factor de conversion if(is_h) { fc = double(m_barras_max_height - HISTOGRAM_GAP_LINE_CORTE) / ptp; m_barras_cordenanda_eje_corte = (m_barras_y1 + m_barras_espacio_dibujable_height); // Y1Real = Corte (no hay separacion) } else { fc = double(m_barras_max_width - HISTOGRAM_GAP_LINE_CORTE) / ptp; m_barras_cordenanda_eje_corte = m_barras_x1; // X1Real = Corte no hay separacion } //--- Set valores m_fr_change_value = m_fr_min_value; m_factor_conversion_value_to_pixel_neg = fc; m_factor_conversion_value_to_pixel_pos = fc; //--- Set funcion de pixel m_function_value_to_pixel = HistogramFunctions::ValueToPixelComplete; //--- Si existe una linea de corte if(CheckPointer(m_line_corte) == POINTER_DYNAMIC) delete m_line_corte; m_line_corte = NULL; //--- Creamos las barras solo pixeles no dibujamos aun (solo actulizamos pixeles) InitCordinates(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogram::InitLienzoBarras(int x1, int y1, int x2, int y2, int gap_init, int gap_end, int gap_espacio_entre_barras, double value_corte_neg, double value_corte_pos) { //--- Chek titulo if(HISTOGRAM_IS_NOT_INIT_TITULO) { LogError("Primero se tuvo que haber seteado el titulo", FUNCION_ACTUAL); return; } //--- Check Y // Nota: El titulo siempre se setea antes de el lienzo siempre if(m_titulo.TituloPosition() == HISTOGRAM_TITULO_ARRIBA) { if(y1 < m_titulo.ReserveY()) // El y1 inicia antes que la altura reservada para el titulo, conflicvto { LogError("Y1 Del titulo inicia antes de la altura reservada para el titulo", FUNCION_ACTUAL); return; } if(y2 > m_height) // FUERA DE RANGHO { LogError(StringFormat("Y2[%d] Final mayor a height[%d]", y2, m_height), FUNCION_ACTUAL); return; } // A que tener en cuenta que el y2 tambien debe de considerar el espacio para los texto fontsize que se puso al moet o de agregar barras // Como es variable no podemos comporarlo // En teoria podriamos metere mas comprobaciones para todos los parametros pero creo que no hace falta } else if(m_titulo.TituloPosition() == HISTOGRAM_TITULO_ABAJO) { const int y2_titulo = m_width - m_titulo.ReserveY(); if(y2 > y2_titulo) // el y2 (eje y de fin) esta mas adelnta que el y2 del titulo, conflicto { LogError("Y2 Del linezo mayor al espacio inferior reservado para el titulo", FUNCION_ACTUAL); return; } if(y1 < 1) { LogError("Y1 Negativo", FUNCION_ACTUAL); return; } // A que tener en cuenta que el y2 tambien debe de considerar el espacio para los texto fontsize que se puso al moet o de agregar barras // Como es variable no podemos comporarlo } //--- m_corte_mode = HIST_CORTE_PERCENT; //--- m_init_flags |= HIST_FLAG_INIT_DATA; //--- m_barras_gap_end = gap_end; m_barras_gap_init = gap_init; m_barras_x1 = x1; m_barras_y1 = y1; m_barras_espacio_dibujable_height = fabs(y2 - y1); m_barras_espacio_dibujable_width = fabs(x2 - x1); m_barras_espacio_entre = gap_espacio_entre_barras; m_percent_corte_neg = value_corte_neg; m_percent_corte_pos = value_corte_pos; //--- const bool is_h = (m_type == HISTOGRAM_HORIZONTAL); //--- Cordenajda de inicial real if(is_h) m_barras_cordinate_init = m_barras_x1 + m_barras_gap_init; else m_barras_cordinate_init = m_barras_y1 + m_barras_gap_init; //--- m_gap_izquierda = x1; // Gap entre el ejey y el "left" de la iamge m_gap_derecha = m_width - (m_gap_izquierda + m_barras_espacio_dibujable_width); // Calculamos el gap de la derecha (faltante) m_gap_inferior = m_height - y2; // Gap entre el ejex (inf) y el "bottom" de la imagen m_gap_superior = y1; //--- OnSetLienzo(); //--- Steamos maximo y minimo SetMaxMin(); //--- Check corte if(m_percent_corte_neg + m_percent_corte_pos != 1.00) { LogCriticalError("Los percentages decimales deben de sumar 1.00", FUNCION_ACTUAL); return; } //--- Maixmo y minimo, y ptp m_fr_max_value = m_bar_max.BarMaxValue(); m_fr_min_value = m_bar_min.BarMinValue(); const double ptp = m_fr_max_value - m_fr_min_value; m_fr_change_value = m_fr_min_value + (ptp * m_percent_corte_neg); // valor de cambio de "neg" a "pos" //--- Funcion de pixel m_function_value_to_pixel = HistogramFunctions::ValueToPixelCorte; //--- Si no existe la creamos const bool exist_pointer = (CheckPointer(m_line_corte) == POINTER_DYNAMIC); //--- Rangos const double range_mayor = m_fr_max_value - m_fr_change_value; const double rango_menor = m_fr_change_value - m_fr_min_value; //--- if(m_type) // Vertical { int width_disponible_neg = int(m_barras_espacio_dibujable_width * m_percent_corte_neg) - HISTOGRAM_GAP_LINE_CORTE; // Espacio disponible para negativos m_factor_conversion_value_to_pixel_neg = double(width_disponible_neg) / rango_menor; // Maximo tamaño de la barra negativa int width_disponible_pos = int(m_barras_espacio_dibujable_width * m_percent_corte_pos) - HISTOGRAM_GAP_LINE_CORTE; m_factor_conversion_value_to_pixel_pos = double(width_disponible_pos) / range_mayor; // Maximo tamaño de la barra positiva //--- Calculaos la cordenada de corte (neg -> pos) m_barras_cordenanda_eje_corte = m_barras_x1 + width_disponible_neg + HISTOGRAM_GAP_LINE_CORTE; //--- if(exist_pointer) // Si ya existe existe { m_line_corte.SetNewValueEjePrincipal(m_barras_cordenanda_eje_corte, true); } else { m_line_corte = new CHistogramLineCorteVer(); m_line_corte.Init(m_canvas); int y1 = m_barras_y1 + 1; int size_corte = m_barras_espacio_dibujable_height - 2; m_line_corte.Set(m_barras_cordenanda_eje_corte, y1, size_corte, 0xFF000000, m_back_color); //negro por defecto } } else // Horizontal { int height_disponible_neg = int(m_barras_espacio_dibujable_height * m_percent_corte_neg) - HISTOGRAM_GAP_LINE_CORTE; // Espacio disponible para negativos m_factor_conversion_value_to_pixel_neg = double(height_disponible_neg) / rango_menor; // Maximo tamaño de la barra negativa int height_disponible_pos = int(m_barras_espacio_dibujable_height * m_percent_corte_pos) - HISTOGRAM_GAP_LINE_CORTE; m_factor_conversion_value_to_pixel_pos = double(height_disponible_pos) / range_mayor; // Maximo tamaño de la barra positiva //--- Calculaos la cordenada de corte (neg -> pos) //------------------------------| Y1 (para horizontal) - GAP m_barras_cordenanda_eje_corte = (m_barras_y1 + m_barras_espacio_dibujable_height) - (height_disponible_neg + HISTOGRAM_GAP_LINE_CORTE); //--- if(exist_pointer) // Si ya existe existe { m_line_corte.SetNewValueEjePrincipal(m_barras_cordenanda_eje_corte, true); } else { m_line_corte = new CHistogramLineCorteHor(); m_line_corte.Init(m_canvas); int x1 = m_barras_x1 + 1; int size_corte = m_barras_espacio_dibujable_width - 2; m_line_corte.Set(x1, m_barras_cordenanda_eje_corte, size_corte, 0xFF000000, m_back_color); } } //--- Creamos las barras solo pixeles no dibujamos aun (solo actulizamos pixeles) InitCordinates(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogram::InitLienzoBarras(int x1, int y1, int x2, int y2, int gap_init, int gap_end, int gap_espacio_entre_barras, double value_corte) { //--- Chek titulo if(HISTOGRAM_IS_NOT_INIT_TITULO) { LogError("Primero se tuvo que haber seteado el titulo", FUNCION_ACTUAL); return; } //--- Check Y // Nota: El titulo siempre se setea antes de el lienzo siempre if(m_titulo.TituloPosition() == HISTOGRAM_TITULO_ARRIBA) { if(y1 < m_titulo.ReserveY()) // El y1 inicia antes que la altura reservada para el titulo, conflicvto { LogError("Y1 Del titulo inicia antes de la altura reservada para el titulo", FUNCION_ACTUAL); return; } if(y2 > m_height) // FUERA DE RANGHO { LogError(StringFormat("Y2[%d] Final mayor a height[%d]", y2, m_height), FUNCION_ACTUAL); return; } // A que tener en cuenta que el y2 tambien debe de considerar el espacio para los texto fontsize que se puso al moet o de agregar barras // Como es variable no podemos comporarlo // En teoria podriamos metere mas comprobaciones para todos los parametros pero creo que no hace falta } else if(m_titulo.TituloPosition() == HISTOGRAM_TITULO_ABAJO) { const int y2_titulo = m_width - m_titulo.ReserveY(); if(y2 > y2_titulo) // el y2 (eje y de fin) esta mas adelnta que el y2 del titulo, conflicto { LogError("Y2 Del linezo mayor al espacio inferior reservado para el titulo", FUNCION_ACTUAL); return; } if(y1 < 1) { LogError("Y1 Negativo", FUNCION_ACTUAL); return; } // A que tener en cuenta que el y2 tambien debe de considerar el espacio para los texto fontsize que se puso al moet o de agregar barras // Como es variable no podemos comporarlo } //--- Print("Hola"); //--- m_corte_mode = HIST_CORTE_VALUE; //--- m_init_flags |= HIST_FLAG_INIT_DATA; //--- m_barras_gap_end = gap_end; m_barras_gap_init = gap_init; m_barras_x1 = x1; m_barras_y1 = y1; m_barras_espacio_dibujable_height = fabs(y2 - y1); m_barras_espacio_dibujable_width = fabs(x2 - x1); m_barras_espacio_entre = gap_espacio_entre_barras; //--- const bool is_h = (m_type == HISTOGRAM_HORIZONTAL); //--- Cordenajda de inicial real if(is_h) m_barras_cordinate_init = m_barras_x1 + m_barras_gap_init; else m_barras_cordinate_init = m_barras_y1 + m_barras_gap_init; //--- m_gap_izquierda = x1; // Gap entre el ejey y el "left" de la iamge m_gap_derecha = m_width - (m_gap_izquierda + m_barras_espacio_dibujable_width); // Calculamos el gap de la derecha (faltante) m_gap_inferior = m_height - y2; // Gap entre el ejex (inf) y el "bottom" de la imagen m_gap_superior = y1; //--- Steamos valores del lienzo OnSetLienzo(); //--- Steamos maximo y minimo SetMaxMin(); //--- Maixmo y minimo, y ptp m_fr_max_value = m_bar_max.BarMaxValue(); m_fr_min_value = m_bar_min.BarMinValue(); //--- Check corte if(value_corte < m_fr_min_value || value_corte > m_fr_max_value) { LogCriticalError("Valor de corte ha superado los limites", FUNCION_ACTUAL); Remover(); return; } //--- Ptp y asigancion a valor de cambio const double ptp = m_fr_max_value - m_fr_min_value; m_fr_change_value = value_corte; //--- Funcion de pixel m_function_value_to_pixel = HistogramFunctions::ValueToPixelCorte; //--- Si no existe la creamos const bool exist_pointer = (CheckPointer(m_line_corte) == POINTER_DYNAMIC); //--- Rangos const double range_mayor = m_fr_max_value - m_fr_change_value; const double rango_menor = m_fr_change_value - m_fr_min_value; PrintFormat("Rango mayor = %.2f | Rango menos = %.2f", range_mayor, rango_menor); //--- Percentages m_percent_corte_neg = rango_menor / ptp; m_percent_corte_pos = range_mayor / ptp; //--- Calc if(m_type) // Vertical { int width_disponible_neg = int(m_barras_espacio_dibujable_width * m_percent_corte_neg) - HISTOGRAM_GAP_LINE_CORTE; // Espacio disponible para negativos m_factor_conversion_value_to_pixel_neg = double(width_disponible_neg) / rango_menor; // Maximo tamaño de la barra negativa int width_disponible_pos = int(m_barras_espacio_dibujable_width * m_percent_corte_pos) - HISTOGRAM_GAP_LINE_CORTE; m_factor_conversion_value_to_pixel_pos = double(width_disponible_pos) / range_mayor; // Maximo tamaño de la barra positiva //--- Calculaos la cordenada de corte (neg -> pos) m_barras_cordenanda_eje_corte = m_barras_x1 + width_disponible_neg + HISTOGRAM_GAP_LINE_CORTE; //--- if(exist_pointer) // Si ya existe existe { m_line_corte.SetNewValueEjePrincipal(m_barras_cordenanda_eje_corte, true); } else { m_line_corte = new CHistogramLineCorteVer(); m_line_corte.Init(m_canvas); int y1 = m_barras_y1 + 1; int size_corte = m_barras_espacio_dibujable_height - 2; m_line_corte.Set(m_barras_cordenanda_eje_corte, y1, size_corte, 0xFF000000, m_back_color); //negro por defecto } } else // Horizontal { int height_disponible_neg = int(m_barras_espacio_dibujable_height * m_percent_corte_neg) - HISTOGRAM_GAP_LINE_CORTE; // Espacio disponible para negativos m_factor_conversion_value_to_pixel_neg = double(height_disponible_neg) / rango_menor; // Maximo tamaño de la barra negativa int height_disponible_pos = int(m_barras_espacio_dibujable_height * m_percent_corte_pos) - HISTOGRAM_GAP_LINE_CORTE; m_factor_conversion_value_to_pixel_pos = double(height_disponible_pos) / range_mayor; // Maximo tamaño de la barra positiva //--- Calculaos la cordenada de corte (neg -> pos) //------------------------------| Y1 (para horizontal) - GAP m_barras_cordenanda_eje_corte = (m_barras_y1 + m_barras_espacio_dibujable_height) - (height_disponible_neg + HISTOGRAM_GAP_LINE_CORTE); //--- if(exist_pointer) // Si ya existe existe { m_line_corte.SetNewValueEjePrincipal(m_barras_cordenanda_eje_corte, true); } else { m_line_corte = new CHistogramLineCorteHor(); m_line_corte.Init(m_canvas); int x1 = m_barras_x1 + 1; int size_corte = m_barras_espacio_dibujable_width - 2; m_line_corte.Set(x1, m_barras_cordenanda_eje_corte, size_corte, 0xFF000000, m_back_color); } } //--- Creamos las barras solo pixeles no dibujamos aun (solo actulizamos pixeles) InitCordinates(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogram::RecalculeFactorsMaxMinCorteVal(void) { m_fr_max_value = m_bar_max.BarMaxValue(); m_fr_min_value = m_bar_min.BarMinValue(); const double ptp = m_fr_max_value - m_fr_min_value; //--- const double range_mayor = m_fr_max_value - m_fr_change_value; const double rango_menor = m_fr_change_value - m_fr_min_value; //--- Percentages m_percent_corte_neg = rango_menor / ptp; m_percent_corte_pos = range_mayor / ptp; //--- if(m_type) // Vertical { int width_disponible_neg = int(m_barras_espacio_dibujable_width * m_percent_corte_neg) - HISTOGRAM_GAP_LINE_CORTE; // Espacio disponible para negativos m_factor_conversion_value_to_pixel_neg = double(width_disponible_neg) / rango_menor; // Maximo tamaño de la barra negativa int width_disponible_pos = int(m_barras_espacio_dibujable_width * m_percent_corte_pos) - HISTOGRAM_GAP_LINE_CORTE; m_factor_conversion_value_to_pixel_pos = double(width_disponible_pos) / range_mayor; // Maximo tamaño de la barra positiva //--- Calculaos la cordenada de corte (neg -> pos) m_barras_cordenanda_eje_corte = m_barras_x1 + width_disponible_neg + HISTOGRAM_GAP_LINE_CORTE; m_line_corte.CleanLine(); } else // Horizontal { int height_disponible_neg = int(m_barras_espacio_dibujable_height * m_percent_corte_neg) - HISTOGRAM_GAP_LINE_CORTE; // Espacio disponible para negativos m_factor_conversion_value_to_pixel_neg = double(height_disponible_neg) / rango_menor; // Maximo tamaño de la barra negativa int height_disponible_pos = int(m_barras_espacio_dibujable_height * m_percent_corte_pos) - HISTOGRAM_GAP_LINE_CORTE; m_factor_conversion_value_to_pixel_pos = double(height_disponible_pos) / range_mayor; // Maximo tamaño de la barra positiva //--- Calculaos la cordenada de corte (neg -> pos) //------------------------------| Y1 (para horizontal) - GAP m_barras_cordenanda_eje_corte = (m_barras_y1 + m_barras_espacio_dibujable_height) - (height_disponible_neg + HISTOGRAM_GAP_LINE_CORTE); m_line_corte.CleanLine(); } } //+------------------------------------------------------------------+ //| Recalculo de factores para corte por percentage | //+------------------------------------------------------------------+ void CHistogram::RecalculeFactorsMaxMinCortePer(void) { m_fr_max_value = m_bar_max.BarMaxValue(); m_fr_min_value = m_bar_min.BarMinValue(); const double ptp = m_fr_max_value - m_fr_min_value; m_fr_change_value = m_fr_min_value + (ptp * m_percent_corte_neg); // valor de cambio de "neg" a "pos" //--- const double range_mayor = m_fr_max_value - m_fr_change_value; const double rango_menor = m_fr_change_value - m_fr_min_value; //--- if(m_type) // Vertical { int width_disponible_neg = int(m_barras_espacio_dibujable_width * m_percent_corte_neg) - HISTOGRAM_GAP_LINE_CORTE; // Espacio disponible para negativos m_factor_conversion_value_to_pixel_neg = double(width_disponible_neg) / rango_menor; // Maximo tamaño de la barra negativa int width_disponible_pos = int(m_barras_espacio_dibujable_width * m_percent_corte_pos) - HISTOGRAM_GAP_LINE_CORTE; m_factor_conversion_value_to_pixel_pos = double(width_disponible_pos) / range_mayor; // Maximo tamaño de la barra positiva //--- Calculaos la cordenada de corte (neg -> pos) m_barras_cordenanda_eje_corte = m_barras_x1 + width_disponible_neg + HISTOGRAM_GAP_LINE_CORTE; m_line_corte.CleanLine(); //--- // m_line_corte.SetNewValueEjePrincipal(m_barras_cordenanda_eje_corte); } else // Horizontal { int height_disponible_neg = int(m_barras_espacio_dibujable_height * m_percent_corte_neg) - HISTOGRAM_GAP_LINE_CORTE; // Espacio disponible para negativos m_factor_conversion_value_to_pixel_neg = double(height_disponible_neg) / rango_menor; // Maximo tamaño de la barra negativa int height_disponible_pos = int(m_barras_espacio_dibujable_height * m_percent_corte_pos) - HISTOGRAM_GAP_LINE_CORTE; m_factor_conversion_value_to_pixel_pos = double(height_disponible_pos) / range_mayor; // Maximo tamaño de la barra positiva //--- Calculaos la cordenada de corte (neg -> pos) //------------------------------| Y1 (para horizontal) - GAP m_barras_cordenanda_eje_corte = (m_barras_y1 + m_barras_espacio_dibujable_height) - (height_disponible_neg + HISTOGRAM_GAP_LINE_CORTE); m_line_corte.CleanLine(); //--- // m_line_corte.SetNewValueEjePrincipal(m_barras_cordenanda_eje_corte); } } //+------------------------------------------------------------------+ //| Recalculo de factor de corte para default | //+------------------------------------------------------------------+ void CHistogram::RecalculeFactorsMaxMinDef(void) { m_fr_max_value = m_bar_max.BarMaxValue(); m_fr_min_value = m_bar_min.BarMinValue(); //--- const double ptp = m_fr_max_value - m_fr_min_value; double fc; //--- if(m_type == HISTOGRAM_HORIZONTAL) { fc = double(m_barras_max_height - HISTOGRAM_GAP_LINE_CORTE) / ptp; m_barras_cordenanda_eje_corte = (m_barras_y1 + m_barras_espacio_dibujable_height); // Y1Real = Corte (no hay separacion) } else { fc = double(m_barras_max_width - HISTOGRAM_GAP_LINE_CORTE) / ptp; m_barras_cordenanda_eje_corte = m_barras_x1; // X1Real = Corte no hay separacion } //--- m_fr_change_value = m_fr_min_value; m_factor_conversion_value_to_pixel_neg = fc; m_factor_conversion_value_to_pixel_pos = fc; } //+------------------------------------------------------------------+ //| Recalculo de los factores | //+------------------------------------------------------------------+ void CHistogram::RecalculeFactorsMaxMin() { switch(m_corte_mode) { case HIST_CORTE_NOT_USE: RecalculeFactorsMaxMinDef(); break; case HIST_CORTE_PERCENT: RecalculeFactorsMaxMinCortePer(); break; case HIST_CORTE_VALUE: RecalculeFactorsMaxMinCorteVal(); break; default: LogCriticalError("No hay tipo de corte", FUNCION_ACTUAL); Remover(); break; } } //+------------------------------------------------------------------+ //| Actuliza el bitmap | //+------------------------------------------------------------------+ __forceinline void CHistogram::Redraw() { m_canvas.Update(true); } //+------------------------------------------------------------------+ //| Crea la parte informativa | //+------------------------------------------------------------------+ void CHistogram::CreatePartInformativa(ENUM_HISTOGRAM_PARTE_INFORMATIVA_POSICION pos, int xgap_inicial, int ygap_inicial) { if(HISTOGRAM_IS_INIT_INFORMATIVE) return; // SOlo se crea una vez //--- m_init_flags |= HIST_FLAG_INIT_PART_INFO; m_part_info_ptr = new CHistogramParteInfo(); m_part_info_position = pos; //--- int x1, x2, y1, y2; //--- if(m_part_info_position == HISTOGRAM_PARTE_INF_DERECHA) { x1 = m_width - m_gap_derecha; x2 = m_width; } else { x1 = 0; x2 = m_gap_izquierda; // Gap izquirdo } //--- y1 = m_gap_superior; // No antes del titulo y2 = m_height; // Hasta el final //--- m_part_info_ptr.Init(m_canvas, x1, y1, x2, y2, xgap_inicial, ygap_inicial); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CHistogram::CreateCopyright(int x, int y, string txt, uint clr, uint align, int fontsize = 0, string font = NULL, uint flagtext = UINT_MAX, uint textpos = UINT_MAX) { // 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_copyright != NULL) return; m_copyright = new CTextCanvas(); m_copyright.Create(m_canvas, x, y, txt, clr, align, fontsize, font, flagtext, textpos); } //+------------------------------------------------------------------+ void CHistogram::CreateCopyrightDefault(const string &autor, string font = "Arial", int fontsize = 15) { MqlDateTime time; TimeToStruct(TimeLocal(), time); CreateCopyright(5, 5, StringFormat("© Copyright %d, %s", time.year, autor), ColorToARGB(clrBlack), TA_TOP | TA_LEFT, fontsize, font, 0, 0); // usar valores por defecto } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CHistogram::SavePicture(const string &filename, bool comon) { const bool res = ResourceSave(m_canvas.ResourceName(), filename); //--- Si es comon movemos de la carpeta files a la direccion comon, si ya esicte reescribimos if(comon) { if(!FileMove(filename, true, filename, FILE_COMMON | FILE_REWRITE)) { const string local = TerminalInfoString(TERMINAL_DATA_PATH); const string common = TerminalInfoString(TERMINAL_COMMONDATA_PATH); const string r_l = local + filename; const string r_c = common + filename; LogError(StringFormat("No se pudo mover el arhcivo %s de:\n%s\na\n%s", filename, r_l, r_c), FUNCION_ACTUAL); } } return res; } //+------------------------------------------------------------------+ #endif //+------------------------------------------------------------------+