Casa abierta al tiempo UNIVERSIDAD AUTONOMA METROPOLITANA - IZTAPALAPA CPJI - 2,' 1 ' " DESARROLLADO : CRUZ GOMEZ HUMBERTO NAVARRO GUTIERREZ MARCO A. RAMOS PENAGOS ALEJANDRO Y México, D.F. Abril de 1995 ""-.A Reporte del Proyecto de InvestigaciónII INDICE INTRODUCCION A LA PROGRAMACION EN WINDOWS . . . . . . . . . . . . . . . . . . . . . . . . . 1 PROGRAMANDO CON EVENTOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 COMPARTIENDO RECURSOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 WINDOWS. OBJETOS Y C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 PROGRAMACION EN WINDOWS CON OBJETOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 OBJETIVODELSISTEMA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 MENUPRINCIPAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 DESCRIPCION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 CARACTERISTICAS TECNICAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 BITMAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 MENU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 DIALOG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 ACCELERATORS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 ARCHIVOSDBF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 LA ESTRUCTURA DE ARCHIVOS DBASE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 LOS DATOS EN DBASE I1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 EL ENCABEZADO (HEADER) EN DBASE I1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 ESTRUCTURAS DE INDICES EN DBASE I1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 LOS DATOS EN DBASE I11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 EL ENCABEZADO (HEADER) EN DBASE I11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 DIFERENCIAS ENTRE ARCHIVOS DE DBASE I1 & DBASE I11 . . . . . . . . . . . . . . . . . . . 13 ANALISIS DE ARCHIVOS DBASE CREADOR POR LA UTILERIA CLIPPER DBU . . . . 13 MODULO DE IMPRESION INTRODUCCION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 CARACTERISTICAS TECNICAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Reporte del Provecto de Investigación II ANALISIS DEL PROGRAMA PRINCIPAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 DONDE COMIENZA LA APLICACION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 USANDO EVENTOS Y MENSAJES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 DISPOSITIVOS DE SALIDAS Y VENTANAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 EL DEVICE CONTEXT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 ATRIBUTOS DEL DEVICE CONTEXT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 ACCESANDO AL DEVICE CONTEXT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 DESPLEGANDO L A S CAPACIDADES DEL DEVICE . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 MODULOSDEGRAFICACION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 GRAFICA DE BARRAS EN 2 DIMENSIONES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 CREANDO UNA GRAFICA DE BARRAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 DESPLEGANDO LA GRAFICA DE BARRAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 ESCRIBIENDO EL TITULO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 DESPLEGANDO ETIQUETAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 DIBUJANDOUNABARRA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 ESTRUCTURA PRINCIPAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 GRAFICA DE BARRAS EN 3 DIMENSIONES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 LA TERCERA DIMENSION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 EL FONDO (BACKDROP) EN TRES DIMENSIONES . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 LOS TOQUES FINALES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 GRAFICADEPIE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 DIBUJANDOUNSLICE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 DESPLEGANDO UNA LEYENDA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 AÑADIENDO BIBLIOGRAFIA . . . . . . . . . . . ................................................. 39 Rem& del Provecto . . I INTRODUCCION A LA PROGRAMACI~NEN WINDOWS La programación en Windows representa un cambio substancial para la mayoría de los programadores gráficos de PC. Windows es la extensión del DOS, así que los programadores pueden tener una consistensia con menÚs, ventanas, interfaz gráficas, acciones con el mouse y cajas de diálogos. Windows hace posible ejecutar múltiples procesos simultáneos y tomar ventaja de toda la memoria disponible en la PC. Windows demanda un nuevo estilo de programación, que a primera instancia puede parecer bastante complicado, pero que entendiendo la filosofia de la programación orientada a objetos, y el entorno de Windows, hacen de esta algo agradable de programar. Windows ofrece mas de 600 funciones a través del API (Application Programmers Interface). Esta extensa libreria nos brinda un entorno inmenso de programación. Este estilo, difiere un poco de lo que estamos acostumbrados a programar. La programación en Windows es diferente por tres básicas razones: 1.- Los programas estan organizados en eventos. 2.- Los recursos de la computadora están compartidas entre aplicaciones. 3.- Los programas no manipulan directamente los dispositivos (como la pantalla). 0 PROGRAMANDO CON EVENTOS En DOS, los programas son usualmente escritos como una secuencia de pasos agrupados en funciones. La programación en Windows, sin embargo, esta organizada alrededor de eventos o mensajes. Un evento ocurre cuando algo dentro de Windows cambia. Mensajes son usados para informar que un evento ha sucedido o esta cerca de ocurrir. Por ejemplo, Windows manda un mensaje llamado WM-LBUTTONDOWN cada vez que se presiona el botón izquierdo del mouse. Un programa, entonces puede ser escrito para responder a un mensaje como WM-LBUTTONDOWN. Una aplicación Windows divide el código, tal que responda a varios mensajes internos manejados por Windows. Por ejemplo, el mensaje WM-LBUTTONDOWN es enviado cuando el botón izquierdo del mouse es presionado, y WM-LBUTTONUP es enviado cuando el botón es liberado. Internamente, los mensajes son simples valores enteros (constantes) que corresponden a los eventos. Las constantes de los mensajes de Windows, son definidos en el encabezado WIND0WS.H 1 Rep- 0 del Po rv- . ., COMPARTIENDO RECURSOS Porque en Windows, múltiples programas pueden ser ejecutados virtualmente al mismo tiempo, los programas en Windows deben compartir los recursos de la computadora, tales como la memoria, el disco, drives, pantallas e impresoras. La compartición de recursos se da de la siguiente forma: pedir permiso para accesar a un dispositivo antes de usarlo, y liberarlo tan pronto como se finalice el trabajo con este. 0 WINDOWS, OBJETOS y C++ Por diseño, Windows está orientado a objetos. Todos los elementos de interface, tales como las ventanas, las barras de scrolls, las cajas de diálogos, etc. estan organizados como objetos. Por ejemplo, un botón OK en una caja de diálogo puede invocar una función que verifique la entrada del usuario y la copie a un conjunto de variables. Como programador de C++, probablemente sea confortable el hecho de que Windows organiza la interface del usuario en elementos dentro de objetos. Sin embargo, Windows no está escrito en un lenguaje orientado a objetos. Consecuentemente, Windows tiene todos los clásicos signos de objetos, sin la sintaxis, tal como C++ PROGRAMACIÓN EN WINDOWS CON OBJETOS EN WINDOWS Para explorar las características orientadas a objetos de C++ mientras se programa en Windows, se necesitará interactuar alrededor de las funciones de Windows. Se pueden desarrollar funciones propias, sin embargo para este proyecto, por comodidad utilizaremos las funciones Windows (OWL), las cuáles están incluidas con el compilador. OWL maneja bastantes puntos de la programación en Windows, y nos da ventaja de las características de la programación orientada a objetos (OOP) de C++. 2 OBJETIVO DEL SISTEMA El proyecto tiene como finalidad desarrollar un sistema editor de gráficos, que sea capaz de graficar archivos DBF e información aleatoria introducida por el usuario. El Sistema, además de graficar la información, proporcionará datos estadísticos de las gráficas tales como medias, varianzas, etc. Contará con una interfaz totalmente bajo Windows para aprovechar las ventajas de las librerias OWL de C++. A continuación se describirán en forma técnica los módulos principales que integran al sistema, así como las herramientas adicionales que se utilizaron para su diseño. MENU PRINCIPAL DESCRIPCION El menú principal del sistema, facilita el acceso a las diferentes opciones de este; todas las opciones del menú principal son activadas por el mouse, y con el uso de teclas rápidas. Por ejemplo, para activar la opción de Archivo, este responde a la acción del mouse y a la combinación de ALT+A. CARACTERISTICASTÉCNICAS El Menu Principal, fué desarrollado en el Workshop, que es una utileria de C++ para diseñar menús, cajas de diálogos, botones, íconos, etc. El archivo que se generó en el Workshop es RUTINAS.RES; este archivo contiene todas los gráficos de interfaz como son los íconos, menus, cajas de diálogos, de herramientas, etc. Se encuentra dividido en cuatro partes, las cuales se detallan a continuación: - BITMAP - MENU - DIALOG - ACCELERATORS 3 del - rP 0 . ., BITMAP En el encabezado de BITMAP se tienen todos los iconos para la generación de los diferentes tipos de gráficas (Barras 2D - 3D,Pie 2D - 3D, etc), así como los íconos de funciones estadísticas con el objeto de que al hacer un clic en el icono, este mande a llamar a su respectiva función. En el código correspondiente al encabezado del bitmap, se tienen números cuyas constantes se encuentran definidas en el archivo 1DENTIF.H. A continuación se enlistan las constantes con una breve descripción de su acción. Como se puede ver en la tabla, los iconos para las diferentes acciones se encuentran duplicados, esto debido a que se necesita un icono (con su respectivo identificador) cuando se presenta en la caja de herramientas, y posteriormente cuando el usuario lo selecciona. Es decir, es el mismo ícono, solamente que en el momento en que es seleccionado, el icono cambia poniéndole sombra para diferenciarlo de los demás, es por esto que cada icono tiene su doble con sombra. 4 En el momento que el usuario selecciona un icono, este cambia PRESSED, para indicar al sistema que ha sido seleccionado (NORMAL ===> PRESSED). 0 M€NU En el encabezado de MENU se tienen todas las definiciones del menú principal del sistema, tales como las de Archivo ---> Abrir, Guardar, Imprimir <---, etc. Estas opciones son las encargadas de : 1. Abrir el archivo .DBF y mandarlo a procesar. 2. Guardar los datos estadísticos generados en la graficación. 3. Capturar datos aleatorios para posteriormente graficarlos. 4. Imprimir las gráficas resultantes. 5. Salir del sistema y regresar al administrador de programas. 6. Mostrar la barra de herramientas que contiene todos los iconos explicados en la sección anterior. 7. Mostrar el menú de ayuda. En la siguiente tabla, se muestran los identificadores con sus respectivas acciones. 0 DIALOG En el módulo DIALOG, se encuentran las cajas de diálogos de Capturar, de AcercaDe, y la caja de Herramientas. En la sección anterior (MENU), se definió como mandar a llamar a estas cajas, en esta se encuentran el diseño de estas. Estas herramientas fueron diseñadas en el Resource Workshop, para ser llamadas posteriormente por el módulo de MENU. 5 - del Provecto . .. La caja de CAPTURAR, accede a cualquier dispositivo físico y lógico de la PC para ser leido; así como a los directorios del dispositivo y muestra los diferentes archivos localizados en el directorio. Permite seleccionar el archivo por medio de un botón, así como su cancelación. La caja de DIALOG-ACERCA, muestra el logo de la UAM, así como la versión del sistema. El DIALOG-TOOLS, es la encargada de contener los iconos (bitmaps) definidos en la seccion BITMAP. Esta caja contiene los espacios para colocar los iconos que acceden a las diferentes acciones. 0 RCCEMRRTORS Esta sección, contiene los FILECOMMANDS, que son las activaciones de teclas rápidas, así como la activación de la tecla Tab. Las teclas rápidas permiten ejecutar una acción sin el mouse. Por ejemplo, para activar el menú Archivo, se puede realizar con el mouse o con las teclas de A/t+A. La tecla Tab permite pasarse de opción a opción; por ejemplo, si se encuentra marcado el botón OK, y se requiere pasar al botón CANCELAR, presione la tecla Tab hasta que el botón requerido se encuentre marcado. Las 4 etapas anteriores, como se menciono anteriormente fueron diseñadas en el Resource Workshop. El archivo que los contiene es el RUTINAS.RES. La extensión RES generada por el Resource, ya se encuentra compilada, por lo que para ligarla con el código fuente de C++, únicamente hay que incluir el archivo RUTINAS.RES en el project UAMIGRAP.PRJ que contiene todos los archivos necesarios para la compilación del sistema. C++ liga el código que se tiene, con el archivo del Resource para generar un Único archivo ejecutable. 6 -del P . ., ARCHIVOS DBF El módulo de lectura de archivos DBF, se encarga de abrir cualquier archivo DBF, leerlo, procesarlo y generar un archivo de salida en modo texto con los registros seleccionados en un formato de 2 columnas (x,y) que posteriormente seran mandados a graficar. El módulo le despliega al usuario todos los campos del archivo, y le solicita que seleccione 2 campos; una vez seleccionados, genera el archivo de salida con todos los registros que incluyen a estos campos. A continuación se describe la estructuras de los archivos DBF, para un fácil entendimiento del módulo. l a estructura de archivos dBase Los datos en dBasell o dBaselll son desplegados en arreglos de columnas y renglones, como en una hoja de calculo. Un ejemplo de este es el que se muestra en la figura 1. Los datos en un campo (o columna) son del mismo tipo (ASCII, numéricos, lógicos, etc.). Cada nombre del campo es Único y cada registro tiene un Único número. los datos en dBase II Los registros de datos comienzan en dBase donde finaliza el encabezado (header). Un byte precede cada registro. El valor de este byte es un espacio en ASCII (20h) siempre y cuando el registro no haya sido borrado, en caso contrario un asterisco (2Ah) representa este byte. No hay separadores, delimitadores o terminadores usados en los registros de datos (data records). 0 El encabezado en dBase II (Header) Los archivos en dBase I I consisten de un encabezado (file header) y un conjunto de registros de datos. Los encabezados proveen información acerca de la estructura de los registros de datos (data records). A continuación se describen cada uno de los bytes que conforman al archivo. 7 R e o w l Provecto . . 8 del Provecto . ., ESTRUCTURA DE INDICES EN JBAS€ II La estructura de indices usadas por dBase II permiten búsquedas secuenciales y por índices. Cada nodo de la estructura de árbol es completado a 512 bytes. El primer nodo es llamado el ancho del nodo. Este contiene información de la llave de la expresión, del nodo raiz, del siguiente nodo disponible y del número máximo de llaves por nodo. Otros nodos son utilizados como valores de las llaves y como apuntadores de nodos inferiores. Los archivos de índices son organizados de la siguiente manera. 9 0 Todos los otros nodos están organizados en la siguiente manera: Byte O Numérico de 8-bits Este es el número de llaves en este nodo. Byte 1-51 1 Arreglo de entradas de llaves. 10 Reo- 0 del Prove- . ., Una entrada de llave esta organizada de la siguiente manera: Byte 0-1 Apuntador al nivel mas bajo. Byte 2-3 Número del registro Byte 4-n Expresión de la llave en ASCII. dBASE 111 0 los datos en dBase 111 Los registros de datos comienzan en dBase donde finaliza el encabezado (header). Un byte precede cada registro. El valor de este byte es un espacio en ASCII (20h) siempre y cuando el registro no haya sido borrado, en caso contrario un asterisco (2Ah) representa este byte. No hay separadores, delimitadores o terminadores usados en los registros de datos (data records). El encabezado en d6ase 111 (Header) Los archivos en dBase II consisten de un encabezado (file header) y un conjunto de registros de datos. Los encabezados proveen información acerca de la estructura de los registros de datos (data records). 11 R e o m del Prove- . . 12 R e o m del Porv- . .. DIFERENCIAS ENTRE ARCHIVOS DE dBASE II & dBASE 111 ANALISIS DE ARCHIVOS JBASE CREAROS POR LA UTlLERlA DE CLIPPER 5.0 (RBU) A continuación se muestran los resultados obtenidos al analizar los archivos .DBF utilizando la utileria DBVIEW del Norton Comander V. 4.0. Como anteriormente se muestra en el análisis de archivos con formatos de dBase, las principales partes de estos archivos son : 1.-Header Size (Tamaño del Encabezado) 2.- Record Size (Tamaño del Registro) 3.- Data (Datos del Archivo) 13 del-orP . .. En la siguiente tabla se muestran los resultados obtenidos de 4 diferentes archivos. Archivo 1 Header Size : 66 bytes Record Size : 38 bytes Número de Campos : 1 Archivo 2 Header Size : 98 bytes Record Size : 21 bytes Número de Campos : 2 Archivo 3 Header Size : 130 bytes Record Size : 21 bytes Número de Campos : 3 Archivo 4 Header Size : 162 bytes Record Size : 40 bytes Número de Campos : 4 Como se puede observar de los análisis mostrados anteriormente, el Header Size es directamente proporcional al número de campos con que cuente el archivo, es decir, si el archivo (.DBF) contiene en su estructura 4 campos, independientemente del número de bytes que ocupen los nombres de estos, y del tipo de información que representen (char, entero, fecha, memo, etc.), el Header Size será siempre de 162 bytes. Con lo que respecta al Record Size, este representa la sumatoria del tipo de campos mas 1 byte. Es decir, si contamos con 4 campos en un archivo .DBF de la siguiente forma: 14 del Prov- Re- P I NOMBRE I Chad351 . .. U 1 El Record Size será de 35 + 8 + 40 + 8 = 91 + 1 = 92 bytes. En lo que concierne a la información (data) de los archivos .DBF, esta se almacena en forma secuencial inmediatamente después de donde termina el Header Size mas un byte. Si seguimos con el ejemplo anterior, se tienen 4 Campos (NOMBRE, MATRICULA, DIRECCION, FECHA-INGRESO), lo que implica un Header Size de 162 bytes, por lo cual la información comenzará en la posición 164 (el byte 164). Como se indicó, la información se almacena en forma secuencial y en formato ASCII, lo cual facilita el acceso a esta. En caso de que el dato de un campo no cubra el ancho del campo, se completa con caracteres NULOS como se indicó en la descripción de archivos de dBase. Otro punto interesante a comentar, es que como se indicó en el análisis de las estructuras de archivos dBase, la separación entre campo y campo es de 32 bytes. Comenzando el primer campo en la posición número 33 (byte 33). Si continuamos con el ejemplo que se ha estado analizando, el campo NOMBRE, aparecería en la posición número 33, es decir la 'NI de NOMBRE se encontraría en el byte 33; el campo MATRICULA comenzaría en la posición 65, el campo DlRECClON en la 97, y el campo FECHA-INGRESO en la posición 129. Cabe recalcar que cuando se hace referencia a posiciones, es el equivalente a bytes. Es decir, si se indica que el campo DlRECClON comienza en la posición número 97 es equivalente a afirmar que el campo DlRECClON comienza en el byte 97. En nuestro caso, lo que mas nos interesa es poder tomar la información de los archivos .DBF, que como ya se mencionó se encuentran secuencialmente después del HEADER SIZE. Por lo que si se conoce la longitud de cada campo (RECORD) , el RECORD SIZE y el HEADER SIZE, la lectura e interpretación de los datos se vuelve relativamente sencilla. 15 MODULO DE IMPRESION 0 INTRODUCCION Se podría decir que imprimir dentro de Windows es simple. El driver de la impresora soportada por Windows, o inmersa con el hardware de la impresora, le dan al programador una gran variedad independiente de acciones sobre la impresora. En Windows se aplican ciertas reglas especiales para el uso de la impresora. La razón es debido a que son varias las aplicaciones que comparten entre sí la impresora, al igual que otros recursos del sistema. Para manejar las impresoras se utilizan los controladores de impresora DDL y que, por lo general, se entregan junto con Windows. A los usuarios no se les proporciona el código objeto de los controladores de la impresora; sin embargo, en la mayoría de los casos no se necesitarán. La forma más sencilla de manejar una impresora es considerándola como un objeto, posiblemente como flujo de datos, en donde se acepten expresiones como la siguiente: printecobject << “imprimiendo ... << end?; If No es complicado construir una clase de impresora que soporte lo anterior, sin embargo su desventaja es el desempeño no es tan bueno cuando se trata de trabajar con gráficas, como es el caso del Sistema a desarrollar. CARACTERISTICASTECNICAS En diversas aplicaciones de Windows se utilizan las bibliotecas de enlace dinámico (DLL). Sus beneficios resultan especialmente Útiles cuando se trata de soportar características ventajosas para aplicaciones a la vez. En vez de poner el código de soporte en cada programa, Windows permite vincular el código a una aplicación dada durante la ejecución misma. Imprimir dentro de Windows es complejo. Se necesita soportar la inicialización de la impresora (orientación, calidad de DPI, alimentación, etc.), manejar multiples salidas a la impresora, informar al usuario el porcentaje de la impresión, y permitir al usuario interrumpir la impresión. La clase que se describe a continuación, soporta todo lo anterior a excepción del porcentaje de impresión. 16 R e m del Porv- . ., La clase Printer requiere un constructor a la ventana del objeto Printer. Este apuntador es almacenado en una variable private. El constructor Printer usa la función miembro De/eteA//() para la inicialización de la impresora; esta función se encarga también de inicializar la variable hDC a O. En Windows, todas las salidas a cualquier dispositivo (pantalla, impresora, plotter, etc.) se realizan a través de un dispositivo de contexto, el cual apunta al dispositivo deseado. Por lo cual, para imprimir un gráfico a la impresora, se necesita que el hDC sea un apuntador a la impresora para lograr la salida deseada. Por ejemplo, para mandar una ellipse a la impresora, la línea que manda la salida a esta sería: Elipse (hDC,x 7, y 7, x2, y2) Si hDC esta apuntando a la impresora, la línea anterior manda la ellipse a la impresora; para lograr la salida de la ellipse a pantalla, hay que hacer que hDC apunte a la pantalla. Lo anterior parece simple, sin embargo para acceder al apuntador a la impresora, se necesitan realizar varias rutinas que accedan al driver de esta, para regresar el apuntador. La idea general de la impresión del Sistema, es crear un objeto del tipo Printer al principio de la aplicación (usando el comando new), usar este objeto cuando se necesite, y entonces eliminarlo (usando el comando delete) cuando la ventana principal se cierre. La ventaja del Sistema, es que actua sobre la impresora que se tenga predeterminada, es decir si se necesita cambiar de impresora, lo Único que hay que hacer es ir al administrador de impresión de Windows, y seleccionar una nueva impresora, en caso de que esta no se encuentre, basta con darla de alta y seleccionarla como impresora predeterminada, y automáticamente el Sistema (UAMIGFWPH) lo reconocerá. A continuación se detallan las principales funciones miembro de la clase Printer. 0 El Constructor de Printer El constructor de la clase Printer, requiere un apuntador a la ventana que esta siendo creada. Este apuntador es almacenado en una variable private. El constructor manda a llamar a la función De/eteA//(),la cual se encarga de inicializar a la impresora. 17 P 0 R e m del Porv- . .. La función miembro DeleteAllO De/eteA// comienza checando si el objeto de la impresora esta imprimiendo un documento (InDocument == TRUE) y detiene el proceso usando la función €núDocurnent() si es necesario. Si el device context de la impresora es valido (hDC > O), este libera el device context actual usando De/eteDC(). De/eteA//() entonces usa la función API de Windows GetProfileString()para leer el profile del archivo WIN.INI, GetProfileString() se encaraa de leer la sección de WIN.INI y reoresar todo que siaue a = (siano igual) en la primera línea en la que comienzan los device=. Esta línea contiene el nombre de la impresora, el nombre de el archivo donde el driver de la impresora se encuentra (sin la extensión .DRV), y el nombre del dispositivo de salida (por eiemplo LPTl:). Para obtener por separado las 3 características anteriores, se utiliza la función strtok() utilizando el delimitador de coma (,) para obtener los parámetros anteriores. Finalmente el Device Context para la impresora es creado usando CreateDCO, GetDeviceCapsO es utilizado para determinar si la impresora puede soportar el manejo de bitmaps. La estructura RECT almacenada en rSize es inicializada con las coordenadas lógicas de la impresora. 0 La función miembro SetupPrinterO Setupprinter es llamada cuando el usuario quiere configurar el driver de la impresora predeterminada por Windows. AI llamar al driver, se pueden seleccionar parámetros como la orientación, los fonts instalados, la memoria disponible, etc. Setupprinter() llama a una función externa (la cual incluye una caja de diálogo externa). El driver de la impresora es requerido para manipular las características de esta. Para accesar a este driver, se comienza utilizando la función LoadLibraryO para cargar el driver de la impresora. Este es el nombre del archivo del driver, obtenido usando la función GetProfileString(), con la extensión .DRV. Entonces se utiliza la función GetProcAddressO para encontrar la función DEVICEMODE dentro de este archivo, y llamar a la función a la dirección obtenida. Esta llamada resulta en la caja de diálogo que esta siendo desplegada, y la información que el usuario introduce en esta, es automáticamente almacenada por el driver de la impresora. Setupprinter() concluye liberando la libreria. 18 ReDQde del Porv- 0 . ., La función miembro StartDocument() StartDocument() utiliza la función Escape() para enviar un SETABORTPROC a la impresora. La función Escape() es utilizada para enviar un STARTDOC a la impresora, el cual manda a la impresora lo que tiene almacenado en el buffer. 0 La función miembro Newpage() La función Newpage() es utilizada para liberar la hoja actual y comenzar con otra. Esta utiliza la función Escape() con el código NEWFRAME, tal y como se indica a continuación. 0 La función miembro EndDocumentO EndDocumentO utiliza el comando de escape ENDDOC para terminar la impresión, tal y como se indica en el siguiente código: 19 - del Prove- . ., A continuación se listan los principales códigos de escape que soporta la función Escape(). SELECCIONANDO LA IMPRESORA DENTRO DE LA APLICACION Recuerde que la clase Print utiliza la impresora predeterminada por Windows en el archivo W h l n i . Para permitirle al usuario que seleccione a que impresora desea la salida, hay que leer la lista de impresoras instaladas en el archivo Win.lni. Para regresar una lista de impresoras instaladas, use NULL como segundo parámetro en la función GetProfileString(). 20 del Po rv- . ., ANALISIS DEL PROGRAMA PRINCIPAL El codigo principal de UAMIGRAPH, se encuentra en el archivo CONTROLXPP, el cual es el encargado de mandar a llamar a todas las demás rutinas tanto internas como externas. A continuación se describen las partes mas relevantes de este módulo. AI final de CONTROL.CPP podemos encontrar la llamada principal al sistema, la cual consta de las siguientes líneas: La clase principal la conforma TUserApplication, la cual es una clase derivada de TApplication. Tambien se utiliza la clase Window. La mayoría de las aplicaciones en Windows, utilizan estas dos clases; a continuación se detallan brevemente cada una de estas. TApplication provee los detalles de la aplicación que Windows necesita; y Window especifica que el programa desplegará y la interacción del usuario. También se pueden observar tipos nuevos de variables, estas nuevas definiciones de variables, son macros o tipos definidos en el encabezado WIND0WS.H. La siguiente tabla muestra algunos de estos tipos y explica brevemente que significan. 21 DONDE COMIENZA LA APLICACION El origen de la aplicación se da en la función WinMain. WinMain instruye a Windows a crear el objeto TUserApplication, el cual ejecuta la función miembro Run, y regresa un status del objeto al sistema operativo. La función RUN esconde el secreto del comportamiento de la aplicación. Esta contiene un bucle While que continuamente pregunta a Windows por el siguiente mensaje a procesar y enviarlo al manejador (handle). El bucle termina cuando el mensaje WM-QUIT ocurre. En muchas formas, WinMain es similar a la función main de C. Como main, WinMain acepta varios argumentos, tales como los primeros dos parámetros, los cuales son usados por Windows múltiples ocurrencias de un programa. El parámetro IpCmdLine apunta a un comando de línea, y nCmdShow indica a Windows como inicialmente debe aparecer la ventana. En realidad, no hay que preocuparse por estos parámetros, ellos son solamente pasados a traves del objeto constructor TUserApplication. Estos son utilizados para que el objeto de la aplicación pueda manejar el estado actual de Windows. OWL maneja todos estos detalles por nosotros. 22 - del Po rv- . .. USANDO €V€NTOSY M€NSAJES Una importante parte del aprendimiento sobre escribir programas en Windows, involucra el concepto de event-driven. Windows responde a eventos que nosotros realizamos. Por ejemplo, cuando presionamos el botón izquierdo del mouse, Windows responde al mensaje WM-LBUTTONDOWN. Por lo tanto, para manipular la acción a este mensaje, lo Único que hay se necesita hacer es sobrecargar las funciones miembros. WML-ButtonDown (accesible a través de la clase Window), la cual corresponde a este evento. DISPOSITIVOS DE SALIDA Y VENTANAS Uno de los principales objetivos del GDI es un dispositivo independiente gráfico programable. El hit es hacer que el código funcione en un display VGA, sin embargo, lo ideal es que funcione también en un display EGA, e incluso en una impresora. Windows se encarga de esto, diseñando las funciones gráficas para trabajar con tipos devices en lugar que sobre un tipo específico de hardware. Windows soporta cuatro tipos de devices: pantalla. impresoras y plotters. bitmaps y archivos especiales conocidos como metafiles, Sin embargo, no todas las salidas tienen las mismas capacidades. Por ejemplo, un plotter no soporta operaciones gráficas para plotting pixels o despliegado de bitmaps. Y algunas impresoras no soportan todos los tipos de gráficas. E l DEVICE CONTEXC Si analizamos el código de UAMIGFWPH, encontraremos un tipo de variable que se repite constantemente, este es del tipo device context, el cual tiene como función primaria lo descrito en los párrafos anteriores. Windows mantiene información acerca de un tipo en particular de device, el cual llama device context, el cual es la liga a la salida a un dispositivo. Actualmente, un device context tiene varios propósitos: Para el manejo de gráficos utilizadas por la aplicación, tales como estilos de fonts, rellenos, bordes, etc. Acceso a rutinas de dibujo y capacidades para un tipo específico de dispositivo. 0 Como mediador entre múltiples aplicaciones que quieren accesar al mismo dispositivo. En una pantalla, el device context se asegura de que la aplicación no 23 dibuje en una zona prohibida. En una impresora, el device context mantiene la operación de impresión separada en el administrador de impresión. RTRIBUTOS DE1 DEVICE CONTEXT Un device context contiene todo lo concerniente a lo que se encuentra dibujando, tales como las funciones que utiliza, el estilo de la línea, el relleno del dibujo y el font que utiliza. La siguiente tabla lista los atributos incluidos en el device context y sus valoreres por default. 24 P ReDQde del Prove- . ., ACC€SANDO A l DEVICE CONT€XT Un device context se encuentra definido en WIND0WS.H de tipo HDC. La siguiente instrucción declara un manejador de tipo device context llamado hDC, tal y como se utiliza en el sistema. HDC hDC; Para obtener acceso a un device en particular, hay dos formas o técnicas manejadas por C++. Una es en respuesta al mensaje WM-PAINT, la cual es generada cuando una ventana es actualizada. En este caso, las funciones Beginpaint y EndPaint son utilizadas para obtener y liberar un manejador al video del device context. Por ejemplo, el siguiente código muestra como estas dos funciones dibujan un rectángulo en la pantalla: La segunda forma de obtener el device context es es utilizando la función GetDC: HDC GetDC (HWND HWindow); Una vez que se ha obtenido el manejador, se pueden mandar todos los gráficos que deseemos. El módulo de impresión de UAMIGRAPH descrito anteriormente, utiliza esta técnica para mandar la salida de los gráficos a la impresora. Como se puede observar, todas las rutinas de dibujo en GDI, requeren de un manejador para ser pasado como primer argumento, e ahí la importancia de lo descrito hasta ahora. Esta es la manera en que Windows se asegura de que se tiene el permiso para acceder a los dispositivos de salida, y saber que dispositivo utilizar. Después de utilizar el dispositivo, es necesario liberarlo. Esto se puede lograr liberando el manejador por medio de la función ReleaseDC. RealeaseDC (HWindow, hDC); 25 Provecto . . I El siguiente código, muestra el uso de las funciones GetDC y ReleaseDC para manejar un rectángulo en respuesta al click izquierdo del mouse. DESPLEGANDO LAS CAPACIDADES DE1 DEVICE Se pueden obtener las capacidades o propiedades del device, y las características que soporta por medio de la función GDI GetDeviceCaps. int GetDeviceCaps (HDC hDC, int Devicecode); También se puede utilizar GetDeviceCaps para determinar cuando un device soporta un tipo particular de operación, tales como una operación de bitmap o el dibujo de una curva. 26 MODULOS DE GRAFICACION 0 GRAFICA DE BARRAS EN 2 DIMENSIONES A continuación se describe el módulo de graficación en dos dimensiones, con sus respectivas clases. TBarGraph se encarga de los detalles de la impresión de la barra en una ventana. La definición para la clase TBarGraph y su código se encuentra en los archivos BARGRAPH.H y BARGRAPH.CPP. Las gráficas de barras creadas, contienen cuatro componentes primarios: un área rectangular, un título, etiquetas en el eje de las X y las Y, y las barras. La clase TBarGraph implementa todo lo que se necesita para inicializar los valores de la gráfica y dibujarla en la ventana. A continuación se muestra la definición de la clase incluida en BARGRAPH.H 27 La clase TBarGraph incluye seis variables privadas que almacenan la información acerca que la gráfica de barras, tales como los valores de los ejes, el título de la gráfica, etc. Algunos de estas variables tienen valores por default que son inicializados en su constructor, sin embargo, todos estos valores pueden ser modificados. La variable BarValues, es un arreglo de entreros, el cual contiene un valor por cada barra. La siguiente tabla, lista cada función en TBarGraph y muestra una pequeña descripción de su comportamiento. CREANDO UN GRAFICA DE BARRAS AI utilizar la clase TBarGraph para dibujar una gráfica, se involucran tres pasos a seguir: 1. Crear e inicializar el objeto TBarGraph. Se puede pasar como parámetro el título de la gráfica al constructor. 2. Colocar las barras en la gráfica llamando a la función TBarGraph::AddBar 3. Llamar a la función TBarGraph::Display para dibujar la gráfica de barras. Primero se debe de crear el objeto TBarGraph de la siguiente forma: TBarGraph *BarGraph = new TBarGraph('%ráficade Prueba") 28 del Po rv- . . La sentencia anterior aloja e inicializa un objeto TBarGraph que es apuntado por BarGraph. El constructor le da el título a la gráfica. Las barras son añadidas de izquierda a derecha, utilizando la función miembro AddBar; por ejemplo para añadir 3 barras a la gráfica se llamaria a la función de la siguiente manera: Elprimer parámetro a AddBar es el valor de la barra, y el segundo es la etiqueta. Losvalores son almacenados en el arreglo Barvalues y las etiquetas son almacenadas en el arreglo de caracteres BarLabels. Por lo cual, cada llamada a AddBar incrementa NumBars, el cual tiene a su cargo el número de barras en la gráfica. NOTA : El constructor a TBarGraph actualmente acepta dos parametros. El primero es el título, el cual es siempre requerido. El segundo parámetro es opcional. Este especifica cuantas líneas horizontales son dibujadas en el fondo. Este valor es almacenado en NumRules; por default el valor es 4, el cual es el número de líneas a dibujar en caso de que ningún otro valor sea especificado: D€SPl€GANDOLA GRAFICA D€ BARRAS Para desplegar la gráfica, se llama a la función de Display en TBarGraph. Normalmente, se debe de llamar a Display en la función Paint. 29 Actualmente la función Display toma tres acciones: 1. Despliega el titulo de la gráfica. 2. Determina el máximo valor de la barra. 3. Llama a Drawchart, el cual dibuja el fondo, las barras y las etiquetas. €SCRIBI€NDO€1TITULO Una gráfica puede tener varios tamaños. Para compensar esto, el título de esta, se ajusta en escala para que se encuentre dentro de las regiones ocupadas por la gráfica. El fonts es seleccionado llamando a la función GDI CreateFont. Una petición es hecha por el tipo de font Roman junto con el alto y el ancho. Un problema puede ocurrir si el título es demasiado largo, sin embargo. Para no tener problemas con textos largos, se llama a la función GDI GetTextExtent, para ver que tan largo es el texto actualmente, tal y como se muestra a continuación: if (LOWORD(GetText€xtent(hDC, Title, strlen(Tit1e))) > (Right-Left*W3) Si el texto es mas largo que dos tercios del ancho de la gráfica, un nuevo tipo de font es seleccionado llamando a la función CreateFont una vez mas. En este caso, el ancho del texto es seleccionado. Después que el titulo ha sido creado, la alineación es modificada para centrar el string, para posteriormente desplegar el título. SetTextAlign (hDC, TA-CENTER I TA-BOTTOM); TextOut (hDC, (Left+Right)/2. Top-2, Title, strlen(Tit1e)); 30 R e D w del Prove- . ., La siguiente sentencia en Display determina el máximo valor a ser desplegado en la gráfica, y guarda este valor en MaxBarValue. Todas las barras son ajustadas al máximo valor. Entonces, la ejecución procede a la función miembro Drawchart, la cual finaliza el dibujo de la gráfica. DESPLEGRNDO ETIQUETAS Las etiquetas a través del eje de las Y y las líneas horizontales son pintadas en DrawChart después de que el fondo es pintado. Recuerde, que el número de etiquetas y las líneas horizontales depende del parámetro NumRules pasados al constructor TBarGraph. Como en el título de la gráfica, las etiquetas para el eje de las Y son ajustadas de acuerdo al tamaño de la gráfica. En este caso, el ancho y la algura del font seleccionado es puesto a un sexto de separación entre las líneas horizontales y los pixeles verticales. Esta separación es calculada en Drawchart y almacenado en la variable Offset. Note que cada etiqueda es justificada hacia la derecha. int Offset = (Bottom-Top) / NumRules; Los strings que aparecen debajo de las barras en el eje de las X, son escritas con el mismo tipo de font. Las etiquetas para las barras, las cuales son especificadas en las llamadas a AddBar, son almacenadas dentro el arreglo BarLabels. Si una etiqueta no se desea para una barra en particulas, su correspondiente valor en BarLabels es puesto en NULL. El loop For localizado al final de Drawchart despliega una barra por cada valor en BarValues, una etiqueta. 31 Dl6UJRNDO UNA BARRA Dentro del loop for, se hace la llamada a la función DrawBar, que es la encargada de desplegar cada barra. Sin embargo, si se examina DrawBar se verá una simple llamada a la función Rectangle. Esto es para hacer posible que otras aplicaciones utilicen TBarGraph para diferentes tipos de gráficas. Introduciendo el código que actualmente dibuja cada barra en la función virtual DrawBar, se puede sobrecargar esta en clases derivadas de TBarGraph para dibujar otros tipos de barras. De hecho, esto es lo que se hace para realizar las gráficas de pie. La altura de cada barra depende del factor en la escala. Este valor se basa en el valor máximo de la barra a dibujar que se encuentra almacenado en MaxBarValue: - Scale = double(i3ottom Top) / MaxBarValue; Para determinar el ancho y el espacio de las barras,.ia idea básica es dividir los ejes horizontales entre las barras a dibujar. La siguiente fórmula determina el espacio de separación y almacena el resultado en Offset: Offset = (Right - LeR) / (NumBars + 7); 32 ESTRUCTURA PRlNClPBl A continuación se detallan el manejo de clases utilizadas en este módulo: La clase TBarGraphWindow derivada de Window, contiene el código que utiliza TBarGraph. Esta clase incluye la variable BarGraph, la cual es un apuntador al objeto TBarGraph. El constructor de TBarGraphWindow dinamicamente aloja e inicializa el objeto BarGraph. La función TBarGraphWindows de Paint llama a la función TBarGraph::Display para dibujar la gráfica: 33 R e D W del Prove- . . GRAFICA DE BARRAS EN 3 DIMENSIONES La gráfica que se desarrolló anteriormente en 2 dimensiones es bastante aceptable, sin embargo, con un poco de creatividad se puede realizar una mejor. Esta es una gráfica de barras en 3 dimensiones que es la que a continuación se detalla. Como Windows no tiene una función que dibuje gráficas en 3 dimensiones. Desarrollaremos la clase TThreeDBar incluida en los códigos de los archivos 3DBAR.H y 3DBAR.CPP. Como la clase TBarChart, esta nueva clase contiene todas las funcionalidad que se necesita para realizar gráficas de 3 dimensiones. Como es de esperarse, realizar una gráfica en 3 dimensiones es un poco mas complicado que una de 2 dimensiones. Para realizar el efecto de 3 dimensiones, se tienen que dibujar cada barra dimensional, crear un fondo en 3 dimensiones, y escribir el código necesario para las etiquetas de la gráfica. A continuación se muestra la definición de la clase TThreeDBar, la cual posee la mayoria de las funciones a utilizar. 34 riÑriDlEND0 lfi T€RC€RA DIMENSION Para barras en dos dimensiones, solamente se necesita especificar la locación, altura y ancho de la barra. Para una barra de tres dimensiones, añadiremos un parámetro de profundidad y color. Como se verá a continuación, para enfatizar la profundidad de las barras, se añadirá sombra a los lados de las barras con un color mas obscuro que el usado en el frente. Parte de la ilusión de la profundidad es creado añadiendo polígonos a los lados y alto de un rectángulo. Estos polígonos son ajustados en un desplazamiento del 25 por ciento del ancho del frente de la barra. int Depth = (Right - Left) / 4; La primera llamada a Polygon en ThreeDBar dibuja una cara de la barra en tres dimensiones. El color de relleno de la barra es pasado a ThreeDBar. El color de los lados, sin embargo es mas obscuro dividiendo los componentes del color RGB de la barra in una mitad, y recombinando estos colores en una brocha: El alto y los lados derechos de la barra de tres dimensiones son desplegados por llamadas a Polygon. E l FONDO (BACKDROP) EN TRES DIMENSIONES Parte de la complejidad de TThreeeDBar es la de realizar el fondo en tres dimensiones. La geometria del fondo es similar a la utilizada para dibujar las barras dimencionales. Los panels verticales son pintados coun una brocha gris clara (LTGRAY-BRUSH) y el piso del fondo con una brocha obscura (GRAY-BRUSH). 35 Los lados de la parte de atras del fondo es dibujado utilizando la función Rectangle y los páneles izquierdo y el de abajo son dibujados con la función Polygon. Debido a que estas dos funciones alinean los pixeles en diferente forma, debemos ajustar para la parte de abajo y la derecha del panel dibujando con Rectangle. Rectangle (hDC, Left + DOffset, Top - Doffset, Right + DOffset + 7, Bottom - DOffset + 7); LOS TOQU€S FINALES Aún se necesita añadir el título y las etiquetas de la gráfica. Para esto, usaremos los fonts por default en lugar de crear fonts dinámicos. En adición, se escribió el código de tal forma que varios renglones de las barras puedan ser desplegados. Como resultado, el arreglo Barvalues esta ahora como un arreglo de dos dimensiones. También, la función GetMax esta provista para buscar en el arreglo Barvalues por el mayor valor de Y . El código de 3DTEST.CPP, contiene las funciones que realizan los gráficos de barras en tres dimensiones. 36 R e . .. C GRAFICA DE PIE Las gráficas de pie, proveen otra tecnica para la graficación de información. A continuación se muestra la clase TPieChart, la cual contiene todos los detalles para dibujar una gráfica de Pie y una leyenda. El código fuente para TPieChart se encuentra en los archivos P1E.H y PIE.CPP. Dl6UJANDO UN SLICE Para dibujar los slice (rebanadas) de la gráfica, se utilizó la función Pie de OWL. Típicamente cada slice representa la proporción, o porcentaje, de una pieza de información en relación a la gráfica entera. Para nuestra aplicación, se asume que se tiene una serie de valores de porcentajes. Cada valor del porcentaje corresponde con un slice en la gráfica, y el total de los slice, representa el 100 por ciento. Claramente, se quiere que la gráfica de pie ha ser desplegada sea circular. Sin embargo, si se utiliza el mapping mode MM-TEXT, no se garantiza esto. Sin embargo, uno de los primeros pasos de la clase, es cambiar al mapping mode MM-ISOTROPIC. SetMapMode (hDC, MM- IS0 TROPIC); SetWndowExt (hDC, rect.right, rect.bottom); SetViewPortt5xt (hDC, rect.righf, recf.bottom); Entonces, el origen es temporalmente cambiado a la posición que se convertirá el centro de la gráfica de pie. SetViewPort (hDC, rect.righü4+ 1O, rect.bottom/2); El radio de la gráfica de pie, es calculado para ser un cuarto del ancho de la ventana. Despues, la función miembro PieceOfPie es llamada para cada slice en la gráfica. Esta función tiene dos responsabilidades: desplegar el slice con el color de fondo apropiado y desplegar un valor de porcentaje a lo largo del slice. 37 RepQdkdel-orP . ., Sin embargo, desplegar el slice se logra con una simple llamada a Pie, se debe de calcular las posiciones iniciales y finales del slice. Estas coordenadas son calculadas como sigue: StartLocX = Radius * cos(;! * PI * Startpercentage / 100.O); SfartLocY = Radius * sin(2 * PI * Startpercentage / 100.0); EndLocX = Radius * cos(2 * PI * (StartPercenfage + SlicePercentage) / 100.O); EndLocY = Radius * sin(2 * PI * (Sfattfercenfage + SlicePercentage) / 100.0); El cálculo dentro de las funciones Seno y Coseno conviertes los valores en porcentajes del slice en radianes basados en su radio: La variable Startpercentage especifica la suma de los porcentajes de los slice. El valor de SlicePercentage es el porcentaje del slice. El resultado de la operación es pasado a la función miembro Pie para dibujar el slice: Pie (hDC, -Radius, -Radius, Radius, Radius, StartLocX, StarfLocY, EndLocX, EndLocy); El color de cada Slice, es almacenado en el arreglo Colors de TPieChart. Determinando donde desplegar los valores de los porcentajes requiere un cálculo similar. Sin embargo, aqui el radio es extendido a un factor de 1.2. De modo, que para compensar por las varias posiciones del texto alrededor de la gráfica de pie, el atributo de justificación del texto, se basa en el ángulo en cual el texto es alojado. D€SPL€GANROUNA LEWNDA AI final de la clase TPieChart, se encuentra la función Display, la rutina ShowKey es llamada para desplegar cada slice. La leganda muestra un rectángulo y una etiqueta por cada pie slice, en el lado derecho de la pantalla. La función Rectangle es utilizada para dibujar cuadros rellenos, los cuales son de 16 pixeles en tamaño. Cabe resaltar, que el mapping mode es puesto a MM-TEXT en la función miembro Display en el momento de realizar los cálculos. 38 U J w n u) w u) a o w n a 3oa P I + o ReD- Porv- . .. BIBLIOGRAFIA Windows Graphics Programming with Borland C++ Loren Heiny John Wiley & Sons, Inc. Programming Windows with Borland C++ William Roetzheim PC Magazine Aplique Turbo C++ Herbert Schildt McGraw -Hill Borland C++ 3.1 Programación Orientada a Objetos Faison Prentice Hall Programación Avanzada de Gráficos e n C para Windows Adams Mc Graw Hill Advanced Graphics on VGA and XGA Card Using Borland C++ Ian O. Angel1 & Dimitrios Tsoubelis Halsted Press 39