universidad autonoma metropolitana …148.206.53.84/tesiuami/uam5567.pdf · 3.4.2- descripción de...

84
? UNIVERSIDAD AUTONOMA METROPOLITANA IZTAPALAPA Ciencias Básicas e Ingeniería Ingeniería Electrónica área de Computación. Proyecto Terminal. Desarrollo de un sistema de reconstrucción de imágenes 3D a partir de cortes. Marzo 1999 Humberto G. Cewantes Maceda 93319407 Asesor: Alfonso Martínez Martínez

Upload: donhan

Post on 30-Sep-2018

214 views

Category:

Documents


0 download

TRANSCRIPT

?

UNIVERSIDAD AUTONOMA METROPOLITANA IZTAPALAPA

Ciencias Básicas e Ingeniería

Ingeniería Electrónica área de Computación.

Proyecto Terminal.

Desarrollo de un sistema de reconstrucción de imágenes 3D a partir de cortes.

Marzo 1999

Humberto G. Cewantes Maceda 93319407

Asesor: Alfonso Martínez Martínez

Documentación del proyecto

nor: Humberto Cewantes Maceda

Asesor : Alfonspklanínez Martinez /

Esta documentación concierne al proyecto terminal realizado por Humberto Cervantes Maceda, bajo la asesoría de Alfonso Martínez Martínez. Fue realizado durante 1998 en el laboratorio de Procesamiento De Señales e Imágenes Biomédicas de la Universidad Autónoma Metropolitana Iztapalapa.

Se puede encontrar una versión electrónica del mismo en la dirección siguiente:

http://itzamna.uam.mx/DOCUMENTACION/Indice.html

Esta documentación fue terminada el 15 de Marzo de 1999.

Dedicado a:

Alfonso Martínez y todo el equipo de LICPOS

Mis padres, Humberto e Ilse y mi hermano Miguel.

Y también a Gaby por estar siempre conmigo.

j Domo Arigato Gozaimashita !

DOCUMENTACION DEL PROYECTO TERMINAL "Sistema de reconstrucción de Imágenes 3D a partir de cortes''

por Humberto Cervantes Maceda .

INDICE: . . 1 .. Introduccion al sistema ................................................................................ 1

2.- La metodología utilizada ............................................................................. 1 2.1 . El macro proceso .......................................................................................... 2 2.2- El micro proceso ........................................................................................... 3

3 .. Especificación y Análisis del sistema ...................................................... 4 3.1 . Requerimientos iniciales ............................................................................... 4 3.2- Situaciones potenciales ................................................................................. 5 3.3- Tarietas CRC ................................................................................................ 5 3.4- Análisis de casos de uso ................................................................................ 7

3.4.1 . Introducción ........................................................................................... 7 3.4.2- Descripción de los casos de uso ............................................................ 8

3.5- Prototipo de la interfaz de usuario ................................................................ 9 3.6- Análisis de las clases .................................................................................... 10

3.6.1 . Determinación de las clases .................................................................. 10 3.6.4- Diccionario de datos de las clases ......................................................... 11 3.6.3 . Diagramas de secuencia ........................................................................ 12 3.6.4- Diagrama de clases inicial ..................................................................... 15

3.7- Conclusión del análisis .................................................................................. 16

4.- Diseño del sistema ......................................................................................... 16 4.1 Patrones de diseño .......................................................................................... 17 4.2 Aplicación de los patrones .............................................................................. 19

4.2.1 El maneiador de ventanas ....................................................................... 19 4.2.2 El procesador de imágenes - ..................................................................... 21

4.3 El área gráfica ................................................................................................ 22 4.4 Refinamientos de la Colección De Datos ...................................................... 23

4.4.1 El arreglo como lista ligada .................................................................... 26 4.5 Diagramas de secuencia ................................................................................ 26 4.6 Diagrama de clases ....................................................................................... 29 4.7 Conclusión de la etapa de diseño ................................................................... 30

-

5.- La Implementación ....................................................................................... 30 5.1 Las clases C ObietoInteractivo y C ObietoModificable .............................. 31 5.2 Encapsulando la interfase de ventanas ........................................................... 31 5.3 Meioras en el área gráfica .............................................................................. 35 5.4 Meioras en la clase C Imagen ....................................................................... 35 5.5 Meioras y otros usos para la lista ligada ........................................................ 36 5.6 La configuración del stack y su uso ............................................................... 37 5.7 Diamamas de secuencia ................................................................................. 37 5.8 Diagramas de clases ....................................................................................... 43

5.8.1 Detalle de clases ..................................................................................... 47 5.9 Conclusión de la etapa ................................................................................... 48

.

6.- Aspectos Técnicos ........................................................................................ 48 6.1 Recuperacion del apuntador 'this' dentro de xforms ...................................... 48 6.2 Pasos a seguir - para implementar una nueva ventana ..................................... 54 6.3 Cambio de contexto para multiples áreas gráficas (Elcanvas) ...................... 63 6.4 Desplegando imágenes con OpenGL ............................................................. 64 6.5 Implementación de 'itoa' en una clase de funciones 'externas'. ...................... 64 6.6 Uso de 'make' para compilar la aplicación ..................................................... 65

- .

7.- Conclusiones .................................................................................................. 68

8.- Bibliografía ..................................................................................................... 69

Introducción. 1 .- Introducción.

En la actualidad estamos viviendo una época de auge en el terreno de la computación, esto es no sólo en el plano tecnológico sino también en el de la creación de aplicaciones o 'software', a diario aparecen nuevos desarrollos cada vez más poderosos pero a la vez sencillos de utilizar. La propuesta de proyecto terminal que se presenta en este reporte concierne el desarrollo de una aplicación para el área de Ingeniería Biomédica, en particular para el laboratorio de procesamiento de señales e imágenes biomédicas (PDSIB) de la UAM - Iztapalapa. Esta aplicación tiene cómo objetivo la representación en forma de imágenes tridimensionales de reconstrucciones hechas a partir de cortes tomográficos a los que se les ha aplicado uno o varios algoritmos.

Las imágenes que se obtienen a partir de estudios como el de resonancia magnética o el de tomografía muestran secciones del cuerpo en imágenes con diversos tonos de gris. Es posible crear un objeto tridimensional que represente alguna estructura interna y que se obtiene a partir de las imágenes, y que se pueda emplear por ejemplo, para visualizar el cráneo ante la necesidad de estudiar una fractura en la cabeza. Las imágenes deben ser procesadas antes de realizar las reconstrucciones, y para ello existe una gran variedad de algoritmos que permiten obtener resultados de mayor o menor calidad. En el laboratorio de procesamiento de señales e imágenes biomédicas se desean probar nuevos algoritmos para la obtención de reconstrucciones tridimensionales, pero hasta ahora no hay manera de hacerlo en las estaciones de trabajo, que permiten desplegar gráficas de manera eficiente, de ahí surge el interés por un sistema como este.

La creación de aplicaciones no es una tarea sencilla, sobre todo cuando estas comienzan a tener un cierto grado de complejidad, y por ello es necesaria la utilización de alguna metodología que permita llevar un orden a lo largo del proceso de desarrollo de la aplicación. En lo que nos concierne, seguiremos la metodología propuesta por Gradv Booch en su libro "Object Solutions".

2.- La metodología.

La metodología con enfoque Orientado a Objetos, que nos propone el autor de este libro, es la del seguimiento de un ciclo de desarrollo iterativo e incremental. Con iterativo se refiere al hecho de que los pasos principales del método deberán ser repetidos varias veces, y esto se realiza de manera incremental esto es que en cada ocasión se refinan los resultados que se tienen de la iteración anterior. Podemos asimilar el proceso a una espiral cuyo diámetro aumenta con el tiempo, pues aparecen cada vez más detalles, esto se llama el "macro proceso'' (proceso global) y en su trayectoria se encuentran una serie de otras pequeñas espirales, que deben ser recorridas para poder continuar el seguimiento de la trayectoria de la espiral principal, eso se llama el "micro proceso" (proceso local).

1

Macro Proceso Micro

9

I t 1 O

--

Progreso

Fig. 1, El macro proceso la serie de micro procesos .

2.1 - El macro proceso.

El macro proceso, o proceso global, cuyo enfoque es principalmente administrativo, comprende el refinamiento sucesivo de la arquitectura del sistema, y está compuesto por los pasos principales del desarrollo tradicional del software (el modelo ‘top-down’), estos son:

Las fases que se muestran en el diagrama consisten principalmente de:

Concepción: Establecimiento de los requerimientos centrales. Análisis: Desarrollo de un modelo del comportamiento deseado del sistema. Diseño: Creación de una arquitectura para la implementación. Evolución: Reestructuración y puesta en acción de la implementación.

Existe también una fase más que no se muestra en el diágrama pero igualmente importante:

Mantenimiento: Administra la Evolución posterior a la entrega.

Debemos recordar que el macro proceso no es una serie de pasos que se recorran una sola vez, sino que debe de realizarse las veces que sea necesario hasta que el problema esté lo suficientemente comprendido como para que se pueda solucionar de manera óptima. La comprensión del problema es definitivamente la tarea mas importante que debe llevar a cabo el equipo de desarrollo pues es a partir de aquí que se edificaran los ‘cimientos’ de la aplicación, esto es la arquitectura, y también se podrán evaluar los riesgos existentes. Cada vez que la arquitectura se completa en una nueva iteración se puede lanzar una versión de prueba que permite apreciar si la ruta que se está tomando es correcta así como la posibilidad de tomar en cuenta detalles que no se hubiesen apreciado antes. Esto presenta claras ventajas respecto al enfoque tradicional en el cual el cliente y el equipo difícilmente podían ver el funcionamiento de la aplicación sino hasta el final del proceso.

2

2.2- El micro proceso.

El micro proceso o proceso local, tiene un enfoque principalmente técnico. El macro proceso provee un contexto para el micro proceso al establecer los productos intermedios y las metas hacia las cuales el equipo se dirige. El macro proceso y el micro proceso no son entidades distintas, están entrelazados y los logros en el proceso global se obtienen a través del proceso local. Las fases de este proceso son las siguientes:

Las fases mostradas en el diagrama consisten de:

Descubrimiento: Búsqueda de las abstracciones que modelen correctamente el problema. Semántica: Determinación de la correcta distribución de responsabilidades entre las clases y los objetos identificados hasta ese momento. Relación: Identificación de las relaciones entre las abstracciones. Implementación: Representación de cada abstracción y mecanismo de la manera más eficiente y elegante.

El micro proceso es también cíclico, es oportunista en el sentido que cada ciclo comienza solamente con aquello que se conoce bien, con la oportunidad de refinar el trabajo que se realiza a cada iteración, se enfoca principalmente en roles y responsabilidades más que en las funciones y en el control y finalmente es pragmático esto es que se cierra un ciclo al armar regularmente porciones reales y ej ecutables.

Posteriormente podremos apreciar el ciclo del micro proceso aplicado al proyecto que se esta describiendo, en particular en lo referente al descubrimiento de clases y sus relaciones.

Los dos proceso anteriores resumen de manera global la metodología que se empleó durante el desarrollo del proyecto. Es importante mencionar que también adoptamos algunas ideas propuestas por otros autores, en particular nos referimos a aquellas que describe Scott W. Ambler en sus papeles acerca del "proceso de modelado orientado a objetos: una aproximación manejada por arquitectura". De este autor en particular retomamos la visión de manera 'serial' del desarrollo. Aunque hemos hecho un énfasis especial en que el proceso es algo iterativo, podemos pensar que cada iteración ocurre de una manera secuencial, que puede ser recorrida en ambas direcciones, y a partir de ello utilizamos la propuesta que nos hace el autor acerca de las técnicas de modelado útiles en cada momento del proceso, para ello podemos referirnos al diagrama siguiente:

3

biagroma de casos de uso

Casos de uso

Prototipo de la inter f . de usuario

biagroma de componentes

Diagrama de

~

+Secuencia

Prototipo técnico Diagrama de datos 1D

(c) 1997 Scot t W. Ambler

El diagrama anterior es útil pues nos permite ubicarnos dentro de la etapa del macro proceso dada la cantidad de diagramas que se necesitan, aunque no todos son indispensables.

3.- Análisis del sistema.

Como hemos visto en la parte introductoria a este trabajo, el primer paso a seguir durante el desarrollo de una aplicación es, antes que sentarse a ‘teclear’, el comprender de la manera mas completa posible las peticiones del usuario respecto a las características del producto que desea. Es evidente que durante esta etapa de desarrollo, la interacción con el cliente es obligatoria, ya que él y las personas que harán uso futuro del producto son los que comprenden realmente las situaciones que pueden presentarse durante la utilización. Primeramente vamos a citar los requerimientos que se nos presentaron, para ello se realizó una pequeña entrevista con el futuro usuario que expuso de manera global cuales eran sus interéses.

3.1 - Requerimientos iniciales.

La obtención de requerimientos se facilita si se tiene un pizarrón disponible, y es recomendable que el cliente realice diagramas y exprese sus ideas de forma ‘natural’ es decir intentando enunciar con fiases sencillas sus necesidades.

Se nos pidió:

Un Sistema de visualización 3D ampliable, esto es que pueda ser adaptable a futuros procesos de calculo. Que se pueda desplegar un stack de imágenes 2D, en su conjunto, ciertas características deben ser tomadas en cuenta, tales como la posibilidad de ver cierta imagen en detalle, o de variar el tamaño del montaje. A la salida se desea obtener un conjunto de datos tridimensionales, o bien la posibilidad de generar una imagen 2D si no se realiza una reconstrucción. El usuario debe pedir datos concernientes al conjunto de imágenes (la distancia, el tamaño ...)

4

Que se pueda ver el montaje antes o después de ser procesado, así como la posibilidad de no perder la imagen original aun después de realizar el procesamiento. Que se desplieguen histogramas de las imágenes. Que se pueda interactuar con las imágenes en su conjunto.

Responsabilidades

En un principio no se ha especificado de manera concreta el tipo de procesamiento que se va a realizar sobre las imágenes, dado que esta herramienta se enfoca a la posibilidad de aplicar diversos tipos de algoritmos, y de poder agregar otros más posteriormente, una cuestión fundamental a tomar en cuenta es que el sistema sea extensible.

Colaboradores

3.2 Situaciones potenciales.

Las situaciones potenciales, o principales que van a ocurrir al utilizar el sistema también se obtienen a partir de la entrevista con el cliente, simplemente se hace una 'recopilación de ideas', es decir, el cliente puede ir diciendo que cosas piensa que pueden suceder, estas se anotan, y posteriormente se reagruparán para el análisis de casos de uso.

Entre las situaciones más importantes que potencialmente se pueden presentar, encontramos:

Cargar datos en memoria. (Hay que introducir la información de los datos al cargarlos). Guardar los datos. Desplegar el stack. (Se debe poder modificar el despliegue para ver detalles u otras cosas). Realizar segmentaciones (incluye configuración de parámetros del algoritmo). Mostrar una imágen 3D. (Incluyendo la interacción con esta.) Mostrar el histograma de una imagen.

Estas situaciones pueden parecer pocas pero involucran en realidad una gran cantidad de procesos para poder ser llevadas a cabo. Recordemos que se trata sólo de las situaciones principales.

Modelo CRC.

El paso siguiente en el análisis de requerimientos de una aplicación es la creación del modelo CRC. De acuerdo con Ambler, el modelo CRC es una colección de tarjetas CRC (Clase - Responsabilidad - Colaborador). Estas tarjetas se dividen en tres secciones que contienen la información del nombre de la clase, sus responsabilidades y sus colaboradores. A continuación se muestra cómo se distribuye esta información.

Una clase es cualquier persona, cosa, evento, concepto, pantalla o reporte. Las responsabilidades de

5

una clase son las cosas que conoce y las que realiza, sus atributos y métodos. Los colaboradores de una clase son las demás clases con las que trabaja en conjunto para llevar a cabo sus responsabilidades.

-No. Imagen -0atos de la Imagen

Crea Copia Elimina

En la práctica conviene tener pequeñas tarjetas de cartón por ejemplo, que se llenarán y que se mostrarán al cliente, de manera que se pueda llegar a un acuerdo sobre la válidez de las abstracciones propuestas.

Histograma

Los pasos a seguir para llenar las tarjetas son los siguientes:

-Tipo de despliegue 2/30 -Parómetros de despliegue

Encontrar clases Encontrar responsabilidades Definir colaboradores Definir casos de uso (situaciones potenciales). Disponer las tarjetas

Stack de Imágenes Volumen 30

Para encontrar las clases debemos pensar qué cosas interactuan con el sistema (en nuestro caso el usuario), y qué cosas son parte del sistema (en un principio se propuso un stack de imágenes, un volumen 3D, un montaje y un proceso de calculo), así como las pantallas útiles a la aplicación (un despliegue de montaje, una entrada de parámetros, un despliegue de volumen y una pantalla general). Una vez que las clases principales han sido encontradas se procede a buscar los atributos y las responsabilidades, para esto se puede formular la pregunta ¿Qué sabe la clase? y ¿Qué hace la clase?. Finalmente se buscan los colaboradores dentro de la lista de clases que se tenga.

Para el proyecto que estamos analizando, las tarjetas CRC que se propusieron inicialmente heron las siguientes:

- Oistancia - Oatos Pac. - Tom. Pixel Cargar Imágenes en Mem Admini s t rar Memor ia

V is ta Gráfica Imagen

I Vis ta Gráfica

IVolumen 30

Cal cu I a Normal es Almacena en memoria datos Reorienta CargaGuardaTraduce dat.

1 imagen I

I Histogroma

-0atos del Histograma Imagen -Parómetros

Calcula Despliega Lee Valor

1 Procesador de Imagenes

I -Tipo de procesamiento I -Datos específicos al PMC I Volumen 3 0 Stack de Imágenes

6

Esta colección de tarjetas representan un intento inicial de encontrar abstacciones útiles para la resolución del problema. Podemos ver que entre otras cosas se busco una abstracción a la Vista Gráfica, esto aparentemente podría no ser tán relevante desde un principio, pero es una buena costumbre separar las cuestiones dependientes de la plataforma (despues veremos esto con más detalle), aparte de que esta abstracción engloba la generalidad de las ventanas que podrán aparecer posteriormente.

La definición de casos de uso implica el estudiar qué tanta posibilidad hay de realizar las situaciones potenciales que el cliente propone mediante las responsabilidades definidas en las tarjetas. Por ejemplo, si una de las situaciones potenciales es la de cargar datos, podemos ver que la abstracción StackDeImagenes tiene prevista esa posibilidad. Esto se debe realizar para las diversas situaciones potenciales.

Se menciona que el último paso a seguir es el de la disposición de las tarjetas, efectivamente, ya que las tarjetas se pueden ordenar de diversas maneras, se debe de aprovechar esto para tratar de encontrar una estructura lógica. Por ejemplo, en el caso anterior nos podemos dar cuenta que el Stack de Imágenes ocupa una posición importante dentro del arreglo, mientras que el histograma no tanto. Esto nos ayudara posteriormente durante la representación del diagrama de clases para encontrar similitudes entre clases o relaciones entre ellas.

3.4- Análisis de casos de uso.

Otra de las herramientas esenciales para obtener una mayor comprensión del problema son los diágramas de casos de uso. A continuación se da una definición y se muestra también como fueron empleados para la realización de la aplicación.

3.4.1 Introducción.

Los casos de uso como los describe Karl E. Wiegers son ''escenarios en los cuales el usuario interactúa con el sistema que se está definiendo para lograr cierto objetivo específico o realizar alguna tarea en particular". El método que seguimos en nuestro desarrollo es el siguiente: a partir de entrevistas con los futuros usuarios del sistema, se intentan descubrir las diversas situaciones que se puedan presentar durante la utilización del sistema. Por ejemplo, si el cliente menciona la necesidad de cargar archivos desde el disco, entonces una de las situaciones de uso potenciales del sistema será: "El usuario carga un archivo de datos". Nótese que los escenarios se describen como enunciados en el lenguaje del cliente. Una vez que se obtienen la mayor parte de las situaciones (recordemos que la naturaleza cíclica del proceso implica que posteriormente podrían aparecer nuevas situaciones que no hubiesen sido contempladas durante la primera aproximación, y que por ello se deben realizar varias entrevistas con el cliente), debemos intentar reagruparlas y encontrar casos de uso generales que abarquen varias de las situaciones antes encontradas. Así, el escenario de caso de uso es una instancia del caso de uso general.

Hemos visto como los casos de uso son escenarios que se pueden presentar durante la utilización del sistema, pero también debemos tomar en cuenta a los actores. Un actor, de acuerdo con la definición que encontramos en el tutorial de UML (www.rational.com), es "alguien o algo que debe interactuar con el

7

sistema en desarrollo". Así, podemos pensar por ejemplo en que un doctor es un actor en un sistema médico. Estos dos componentes, actores y casos de uso serán entonces representados en diagramas llamados 'diagramas de casos de uso'. En estos diagramas se muestran los actores y los casos de uso, así como las relaciones que existan entre ellos, pues no todos los actores están relacionados con todos los casos de uso. También existen relaciones entre los casos de uso, destacan dos tipos distintos, aquellas de <uso» y las de «extensión». Las relaciones de «USO» muestran comportamientos que son comunes a uno o más casos de uso, las relaciones de «extensión» muestran comportamientos opcionales.

3.4.2- Descripción de los casos de uso.

Enseguida veremos como se aplicó lo antes mencionado dentro del proyecto.

Para comenzar, retomamos las situaciones potenciales de utilización del sistema, pero detallandolas más, primero se muestra el nombre del caso de uso y enseguida sus instancias:

Caso de uso: Transferencia de Datos

- El usuario introduce un stack de imágenes. - El usuario guarda un stack de imágenes (procesadas o no). - El usuario guarda los datos de la reconstrucción (volumen). - El usuario carga una reconstrucción.

Caso de uso: Seleccionar Parámetros

- El usuario introduce los datos del stack de imágenes. - El usuario selecciona un tipo de procesamiento y los parámetros específicos al proceso. - El usuario selecciona parámetros diversos del despliegue.

Caso de uso: Procesar Datos

- El usuario realiza una segmentación. - El usuario realiza una reconstrucción.

Caso de uso: Interactuar con Datos

- El usuario interactúa con la imagen 3D (rotación...). - El usuario interactúa con la imagen 2D (selección ...) - El usuario interactúa con el histograma.

Caso de uso: Desplegar Datos

- El sistema despliega datos en forma de montaje (2D).

8

- El sistema muestra datos en forma de imagen 3D. - El sistema muestra datos en forma de histograma.

En lo que concierne a los actores, para nuestro proyecto solo tenemos un actor que es el usuario.

El diagrama a continuación muestra los casos de uso ,sus relaciones así como al actor antes mencionado.

a Transferencia de Datos

3.5- Prototipo de la interfaz de usuario.

Es importante presentar al futuro clientehsuario un prototipo de la interfaz que tendrá la aplicación. Para ello se puede hacer uso de los diágramas de flujo de la interfase, que muestran que interfases irán apareciendo dependiendo de las acciones que tome el usuario.

En este caso, el requerimiento para la interfaz de usuario es sencillo, básicamente se trata de una ventana con un área gráfica en donde se verán las imágenes con las que se esta trabajando y un menú a partir del cual se pueden realizar las acciones tales como cargadguardar, seleccionar procesos y ayuda.

9

Posteriormente se tendrán que agregar nuevas ventanas ya que cada proceso necesita ser configurado de manera distinta.

Para realizar un prototipo simplemente se debe de dar al usuario una idea de la apariencia de la aplicación, esto es como un 'esqueleto' no funcional, para facilitar esto es recomendable hacer uso de alguna herramienta de constmccion de interfases de usuario, tal como Visual C++, o en nuestro caso "form design", de xforms.

3.6 Análisis de clases.

Otro paso fundamental dentro de la etapa de análisis del sistema es la de la realización de un diagráma de clases 'inicial', en realidad este diagrama retoma las tarjetas CRC que se realizaron anteriormente, tomando en cuenta su disposición, así como el estudio de los casos de uso y finalmente el estudio a nivel generalizado del comportamiento en el tiempo de los objetos instanciados a partir de las clases, como veremos adelante.

3.6.1 Determinación de las clases.

Lo que se hace para pasar de las tarjetas a clases, es tomar cada tarjeta, y crear una clase para ella. Los atributos y responsabilidades de la clase se encuentran a partir de las responsabilidades de la tarjeta CRC. Las relaciones entre clases aparecen a partir de los colaboradores. En este momento también pueden descubrirse atributos comunes entre abstracciones y en ese caso es conveniente pensar en alguna abstracción a nivel superior de la cual hereden las clases que compartan atributos.

En nuestro caso podemos pensar que el Volumen 3D, el Stack de Imágenes y el Hístograma son todos colecciones de datos, que comparten responsabilidades tales cómo el desplegarse, cargar datos y almacenarlos. Por ello desde un principio decidimos reagruparlos al hacerlos heredar de una clase abstracta llamada Coleccion de Datos. Esto permitirá posteriormente simplificar el estudio de las instancias de esta clase, por ejemplo al realizar el diágrama de secuencia de cargado de datos, pues todas las clases hijas lo realizarán de la misma manera.

A continuación se mencionan las clases propuestas con detalle.

3.6.2 Diccionario de datos de las clases.

El diccionario de datos de las clases simplemente detalla cada una de las clases que se han descubierto, se muestra el nombre de la clase (que por convención se hace con un 'C-' y el nombre con mayusculas en sus letras iniciales y sin espacios), sus atributos y sus operaciones. No se da aún un tipo de dato para los atributos pues esto es parte del diseño. (Esto se hizo siguiendo un ejemplo de la Universidad de Munich.)

Nota: Dentro de las operaciones no se muestran aquellas que permiten tener acceso a los atributos, se supondrá que para cada atributo hay operaciones que realizan dicha tarea. Otra cosa importante es que C Punto y C Color no habian sido descubiertas como abstracciones originalmente, pero dado que el vzúmen y echistograma son clases instanciadas a partir de CColeccionDeDatos, necesitan hacer uso de otra clase para su instancia, por ello se incluyen en el diccionario. La aplicación es una abstracción general del sistema y es conveniente representarla desde el inicio.

Nombre: otras). Atributos: Datos Un arreglo de datos de tipos variados. Operaciones: CargaDatos Carga los datos a partir del disco.

GuardaDatos Guarda los datos en el disco. CambiaDatos Cambia los datos. DespliegaDatos Despliega los datos. Configura Configura la colección si es necesario.

C - ColeccionDeDatos Una colección de datos (es una clase abstracta de donde se derivan

Nombre: Atributos:

C-StackDeImagenes Una colección de imágenes. Hereda de C-ColeccionDeDatos. NumeroDeImagenes El número total de imágenes del stack. TamanoDeImagen El tamaño de la imagen (en puntos). DistanciaEntreImagenes La distancia entre dos cortes (en mm). DatosDelPaciente Pequeño texto para datos útiles. TamanoDelPixel Tamaño de un pixel (en mm). TamanoDelDato Formato de imágen (bytedpixel).

Nombre: C Imagen Una imagen. Atributos:

Operaciones:

DatosDeImagen Información que define la imágen NumeroDeImagen Un número que identifica la imágen dentro del stack. Copia copia los datos de la imagen a otra o desde otra.

Nombre: Atributos:

C volumen3D Una colección de puntos en el espacio 3D. ÑumeroDePuntos El número de puntos del que se compone el volúmen. Orientación La orientación del volúmen en el espacio. Normales Las normales a los puntos.

Operaciones: Cambiaorientación cambia la orientación del volumen3D

Nombre: Atributos: CoordX La coordenada X

CoordY La coordenada Y CoordZ La coordenada Z

C-Punto Un punto en el espacio tridimensional.

Nombre: Atributos: Operaciones:

C-Histograma La información de una imagen representada como histograma. ImagenFuente La imagen a la cual se le calcula el histograma. Calcula Calcula el histograma para la imagen dada.

Nombre: Atributos:

C Color Un color definido como tres niveles, R, G y B. ÑivelR La componente roja del color. NivelG La componente verde del color. NivelB La componente azul del color.

Nombre: Atributos: Operaciones:

CProcesadorDeImagenes Se encarga de aplicar procesos sobre colecciones de datos. TipoDeProceso Identifica el tipo de proceso que se va a aplicar. AplicaProceso Aplica un algoritmo o proceso determinado. SeleccionaProceso Selecciona uno de los posibles procesos a aplicar. Configura Asigna los parámetros necesarios al proceso.

Nombre: Atributos: Operaciones: Abreventana Abre una ventana.

C VistaGrafica Se encarga de toda la interaccion visual. TipoDeDespliegue Define el tipo de interfase a presentar.

EsperaInteraccion Recibe la interaccion. EnviaDatosAVentanaGrafica Despliega datos de la coleccion.

Nombre: C-Aplicacion Operaciones: Inicia Inicia la aplicación.

3.6.3 Diagramas de secuencia.

Hemos definido las abstracciones iniciales para la aplicación, pero debemos encontrar cual será el tipo de relación que existe entre estas abstracciones. Una herramienta útil para aclarar este punto son los diagramas de secuencia. Estos diagramas muestran los mensajes que se intercambian las instancias de las clases durante el tiempo. Desde un principio intentamos aclarar qué tipo de relación existe entre las abstracciones. (Véanse algunos conseios).

Una aproximación uti1 es de realizar un diagrama de secuencia por cada caso de uso o que sea representativo de cada uno de estos. En nuestro caso vamos a escojer los siguientes:

Cargar o Guardar una colección de datos (de Transferencia de Datos).

12

Interacción y despliegue de los datos . Aplicar un proceso (involucra Seleccionar Parametros y Procesar Datos).

Normalmente los mensajes que se ven en los diagramas de secuencia deben corresponder estrechamente con las operaciones disponibles para cada clase, pero en esta etapa en la que aún se está buscando comprender bien el problema, pueden aparecer mensajes textuales que posteriormente serán refinados y corresponderan a funciones concretas de cada clase.

Se presenta primeramente un diagrama 'general' de l a aplicación que muestra de manera global la interacción entre el actor y la aplicación y enseguida se muestran los demás.

Los diagramas de secuencia son los siguientes:

R : Usuario

I I I I I I

I I

I I I

I "Pide datos del stack" I I

"Abre un archivo" Abreventana( )

--. /-

I I n- I CargaDatos( 1 'r' I I

7 I

DespliegaDatos( 1 I

I I I I I I I I I I I I I

I I I I I I I I I I

13

Diagrama de secuencia Cargar/Guardar Datos.

Vista: C Vista R

: Usuario

I I I I I I I I I I I I I I I I I Esperalnteraccion( )

U I I I I I I I I I

C ColecciónD eDatos

I I I I I I I I I I I I

CambiaDatos( ) \ i EnviaDatosAVentanaGrafica( )

I I I I I I I I I I I I

Diagrama de secuencia: Interactuar y desplegar datos.

14

A Delmaqenes : Usuario

I I I

"Aplica Proceso" \ .-- I

Abreventana( ) \

I I Espe,ralnteraccion( 1

I SeieccionaProceSo(

I \

I I I "Introduce Parametros"

I Configura( I I rP I

I I

J I I I I I I I I I

I

I :p l icaPjoceso( ) I

CambiaDatos() I

I I I I I I I I I I I I I I I

I IP I I I I I I I I I I I I I I I I

I

EnviaDatosAVentanaGrafica( )

'u 1 I I I I I I I

Diagrama de secuencia: Procesar Datos.

3.6.4- Diagrama de Clases

Terminamos la etapa de análisis con un diagrama de clases que muestra las abstracciones mínimas propuestas para la resolución del problema, así como las relaciones que existen entre ellas.

El diagrama es el siguiente.

15

^-- " - -- - -

, '*,

'<

3.7 Conclusión del análisis.

El análisis es una etapa fundamental dentro de la realización de una aplicación, esta etapa se puede resumir en una sola fiase: Entender el problema. Cuando terminamos el análisis tenemos ya una comprensión mayor del problema, sabemos cuales son las abstracciones claves, y empezamos a estudiar como se desenvuelve la aplicación en el tiempo. Es aconsejable documentar esta etapa incluyendo en la documentación los varios resultados como los que obtuvimos aquí.

La etapa siguiente es la del diseño, cuyo objetivo principal es de construir un diagrama de clases mas detallado, refinado y orientado a la implementación partiendo del diagrama de clases de análisis, y es la que se verá a continuación.

4.- Diseño del sistema.

Comenzaremos la parte del diseño citando a G. Booch :

"El propósito del diseño es de crear una arquitectura para la naciente implementación, [ .. .] el diseño arquitectura1 sólo puede comenzar una vez que el equipo tenga un entendimiento razonable de los requerimientos del sistema. [...I El diseño, como el análisis, nunca termina realmente hasta que el sistema final es entregado. Durante esta fase, se alcanza un cierto grado de culminación al poner en su

16

lugar la mayoría de las decisiones estratégicas de diseño y al establecer políticas para diversos problemas tácticos. [...I El diseño se enfoca en la estructura, estática y dinámica, su propósito principal es de crear el 'esqueleto' concreto del sistema sobre del cual todo el resto de la implementación se basa."

Estas palabras definen claramente qué es el diseño, la creación de la estructura básica del sistema es la tarea clave, aunque también se buscan otras cosas, en particular patrones que simplifiquen el diseño y posibilidades de reuso entre otras.

Durante la etapa de diseño vamos a refinar las partes fundamentales para la aplicación. Para lograrlo, es conveniente comenzar a buscar patrones de diseño, a continuación describimos esto de manera más detallada.

4.1 Patrones de diseño.

De acuerdo con el libro 'Design Patterns' , un patrón es una "solución simple y elegante a problemas específicos dentro del diseño de aplicaciónes orientadas a objetos, los patrones capturan soluciones que han sido desarrolladas y han evolucionado a lo largo del tiempo, de ahí que normalmente no son el tipo de soluciones que se tienden a generar de manera inicial, reflejan el trabajo de los desarrolladores que han luchado por obtener una mayor posibilidad de reuso y flexibilidad en su software. Los patrones de diseño capturan estas soluciones de una manera fácil de aplicar."

Una vez que se ha terminado con el análisis, conviene detenerse a estudiar el resultado principal de esa etapa, es decir el diagrama de clases de análisis. Es importante buscar que partes pueden ser mejoradas mediante el uso de algún patrón de diseño. Para ello es útil consultar algun catálogo de patrones y mediante sus descripciones tratar de encontrar alguno que se ajuste o que provea alguna solución al problema que se tiene.

En nuestro caso particular, buscamos en el libro 'Design Patterns' y al leer las descripciones nos encontramos con la siguiente:

Estrategia (315) Define una familia de algoritmos, encapsula cada uno, y los hace intercambiables. La estrategia permite que los algoritmos varien independientemente de los clientes que los utilizan.

Al mirar de manera más detallada su descripción, vemos que se recomienda aplicar este patrón entre otras cosas cuando:

Varias clases relacionadas difieren sólo en su comportamiento. La estrategia provee una forma de configurar una clase con uno o más comportamientos. Se necesitan multiples variantes de un algoritmo. Un algoritmo usa datos que los clientes no deben conocer. El patrón puede ser utilizado para evitar exponer complejas estructuras de datos específicas al algoritmo.

Si recordamos los requerimientos de la aplicación, nos viene a la mente el hecho de que es importante poder aplicar algoritmos diversos, y se deben poder agregar más posteriormente. Este patrón parece ofrecer una solución adecuada al problema que se nos presenta. Continuemos estudiandolo con más detalle.

El diagrama de clases del patrón es el siguiente:

17

, I

Contexto --.

Estrategia

InteifaseDelConte~o()I I lnteríaseDelAlgoritmo() I A

lnterfaseDelAlgoritmo()

r I

lnterfaseDelAlgoritmo() I I lnterfaseDelAlgoritmo() I I EstrategiaConcretaA I I EstrategiaConcretaB I I EstrategiaConcretaC I

Los participantes son:

Estrategia - Declara una interfase común a todos los algoritmos soportados. El Contexto usa esta

Estrategiaconcreta - Implementa el algoritmo usando la interfase de estrategia. Contexto - Se configura con un objeto Estrategiaconcreta, mantiene una referencia al objeto Estrategia y puede definir una interfase que permita que Estrategia tenga acceso a sus datos.

interfase para llamar al algoritmo definido por una Estrategiaconcreta.

La colaboraciones son:

Estrategia y Contexto interactuan para implementar el algoritmo deseado. Un contexto puede pasar todos los datos requeridos por el algoritmo a la Estrategia cuando el algoritmo es llamado. De manera alternativa, el contexto se puede enviar a si mismo como argumento a las operaciones de Estrategia, esto permita que la Estrategia llame al Contexto cuando lo requiera. Un contexto pasa las peticiones de sus clientes a su estrategia. Los clientes normalmente crean y pasan un objeto Estrategiaconcreta al Contexto; de ahí que los clientes interactuen con el contexto exclusivamente. Normalmente hay una familia de clases Estrategiaconcreta de las cuales un cliente puede escoger.

Funcionamiento :

Este patrón funciona de la manera siguiente: Al crear la instancia del contexto, este instancia a su vez alguna de las EstrategiasConcretas. Ahora cuando se llama a InterfaseDeContexto, esta a su vez llamara a InterfaseDeAlgoritmo y automáticamente se seleccionará la interfase deseada, sin necesidad de recurrir a sentencias condicionales (switch-case) para la seleccion del algoritmo correcto.

usos:

Este patrón se adapta bien al problema de aplicar múltiples algoritmos. También puede ser utilizado para la creación de ventanas.

Problemas:

La desventaja que presenta este patrón para el problema que se nos presenta es que este patrón se limita a la selección de una sola de las estrategias que queda intimamente asociada al objeto Contexto. En nuestro caso en particular, no deseamos que sólo se pueda crear un algóritmo que se quede instanciado durante toda la aplicación. Sin embargo, la idea de presentar una interfaz común a todos los clientes es muy interesante, así que proponemos una ligera modificación al patrón, cuyo diagrama de

18

clases se muestra a continuación:

Estrategia -----

lnterfaseDelContexto() lnterfaseDelAlgoritmo()

A

[-terfaseDelAlgoritmo() 1 1 I nterfaseDeiAlgoritmo() I [InterfaseDelAlgoritmo() 1

Como podemos apreciar, ahora la relación que existe entre el Contexto y la Estrategia ya no es una agregación, sino una relación de uso. Esto sucede por que en la operacion InterfaseDelContexto se crea la instancia de la Estrategia, y esta se destruye al terminar la operación, de ahí que sea una relación de uso. El Cliente tiene una referencia del Contexto y así puede acceder a su InterfaseDelContexto. Cuando un cliente hace uso de esta interfase, pasa una referencia de sí mismo de tal forma que el Contexto a su vez se la transmita a la Estrategia al instanciarla. El resultado es que cualquier Estrategiaconcreta conoce a su cliente y puede acceder a sus operaciones.

A diferencia del patrón Estrategia convencional, este es más flexible, pues el Contexto se instancia una sola vez (probablemente por la aplicación), y este se encarga de crear y destruir las instancias de Estrategiaconcreta que cree, pero se pueden solicitar las diversas estrategias cuantas veces sea necesario, y el cliente puede ser cualquiera que tenga una referencia al contexto.

4.2 Aplicación de los patrones.

Una vez que hemos encontrado soluciones aplicables al problema que estamos tratando de resolver las tenemos que incorporar al modelo resultante del análisis. Si estudiamos el diágrama de clases propuesto en la parte de análisis, notamos que existen tres 'partes' principales en el problema; estas son:

La colección de datos y las clases que se derivan de ella. La vista gráfica que se encarga de la interacción con el usuario y el despliegue. El procesador de imágenes que se encarga de la modificación de los datos.

Primeramente estudiaremos cómo se aplica el patrón descrito anteriormente en la vista gráfica y en el procesador de imágenes, afortunadamente fue posible aplicar el mismo patrón para resolver dos problemas aparentemente distintos como se describe a continuación.

4.2.1 El maneiador de ventanas.

19

Uno de los puntos que recalcamos desde el inicio del análisis fue el de tener una abstracción para la vista que llamamos VistaGrafica. La ventaja de hacer una abstracción de la parte interactiva es que podemos separarla de las demás clases, y sobre todo, podemos aislar el código que depende de la plataforma, puesto que normalmente las interfases de usuarios son una de las partes más dependientes de la plataforma que hay en la aplicación.

Basándonos en el patrón estrategia, que 'define una familia de algoritmos, encapsula cada uno y los hace intercambiables de tal forma que los algoritmos varien de manera independiente de los clientes que los usan', realizamos una interfase común a todas las ventanas, esta interfase la provee la clase 'ManejadorDeVentanas'. A continuación se muestra el diagrama de las clases que involucran esta solución.

Podemos ver que se tiene la misma situación que se describió anteriormente en el patrón Estrategia modificado.

La aplicación crea la instancia del manejador, que será único en la aplicación. Cualquier clase que necesite algun tipo de interacción por parte del usuario tiene que conocer al manejador de ventanas, la aplicación se puede encargar de proveer una referencia a este. Se muestra aqui que la C ColeccionDeDatos tiene una referencia al manejador, así, cualquier clase instanciada a partir de esta podrá pedir interaccion.

La interfase común del manejador es la función Seleccionaventana(), a través de esta función cualquier objeto puede pedir que se cree una ventana. Aunque no se muestra aquí, la ventana tiene una referencia de la clase cliente, de tál forma que pueda comunicarse con él.

Una vez que se llama a SeleccionaVentana(), el manejador sigue siempre una serie de pasos idénticos:

1 .- Instanciar la ventana, dandole a conocer a su cliente. 2.- Abrir ventana 3.- Activarla (entrar al ciclo de espera de eventos y recibir interacción por parte del usuario). 4.- Cerrar la ventana. 5 .- Destruir la instancia.

20

Al activarse la ventana, esta espera interacción por parte del usuario, y dado que tiene una referencia a un cliente, puede modificar atributos de la clase cliente. Una ventaja de esta solución es que un cliente puede pedir varias ventanas diferentes, otra es que el código específico de las ventanas se queda dentro de la implementación de la ventana y el cliente no tiene ningún conocimiento de cómo se realiza esto.

C-ProcesadorDelmagenes

9 --- Asig naM anejador () Se1 eccionaProceso() C-ProcesadorDelmagenes()

4.2.2 El procesador de imágenes.

C-Proceso

Asignacliente() AsignaManejadorO inicializa() EjecutaProceso() C-Proceso()

Otra parte en donde el patrón expuesto anteriormente facilitó una solución elegante fue en la aplicación de algoritmos. De la misma manera que para las ventanas, es necesario poder crear diversas instancias de los algoritmos y aplicarlas a clientes distintos.

El diagrama de clases que muestra como quedó esta solución es el siguiente:

C-Aplicacion Y

I

En este diágrama la relación entre dos procesos que tienen como cliente al Stack de imágenes, pero debemos recordar que el cliente no forzosamente es de un tipo Único.

De igual forma que el ManejadorDeVentanas sigue unos pasos establecidos al instanciar una ventana, el ProcesadorDeImagenes también sigue una serie de pasos cuando se le solicita algún proceso a través de SeleccionaProceso(), estos son:

21

1 .- Instanciar el proceso. 2.- Darle a conocer su cliente. 3.- Darle a conocer el manejador de ventanas. 4.- Inicializar el proceso. 5.- Ejecutarlo. 6.- Destruir la instancia del proceso.

Es interesante notar que los procesos normalmente requieren interacción por parte del usuario, es por ello que se necesita 'darles a conocer' al manejador de ventanas, para que puedan solicitar una ventana.

En lo que se refiere a patrones, esto es todo lo que se empleo durante el diseño. Esto no significa que no se puedan encontrar más patrones utiles para la aplicación, y de hecho, se debe tratar de emplear la mayor cantidad posible de ellos, pues como ya mencionamos anteriormente, son soluciones ya probadas y de ahí que sea ventajosa su utilización.

4.3 El área gráfica.

Hemos visto en el punto anterior como se ha ampliado la abstracción relacionada con la vista. Sin embargo, hay dos aspectos distintos en lo que concierne a las vistas, el primero son las ventanas a través de las cuales se recibe interacción por parte del usuario, pero dentro de estas puede encontrarse un área en donde se desplieguen gráficas. Puesto que desde un principio se penso aprovechar el hecho de que la plataforma en la que se deseaba implementar la aplicación posee hardware de aceleración de gráficas, y pensando también en aspectos de portabilidad a futuro, es conveniente separar todo lo relacionado a la graficación en una nueva abstracción. Esta abstracción encapsula todos los comandos que se encarguen de realizar gráficas. Otra ventaja de hacer esto es que se pueden cambiar las librerias de gráficas o de interacción sin que se afecten mutuamente. Es interesante notar que normalmente las librerias de interfases con el usuario no tienen la capacidad de dibujar gráficas de alto rendimiento.

De ahí que hayamos decidido tener una clase llamada C-AreaGrafica. Esta clase se encarga de administrar todo lo que aparece dentro de las áreas de dibujo. Todas las ventanas que necesiten desplegar gráficas tendrán una instancia de esta clase (o varias si es necesario).

La colección de datos también tendrá una referencia a esta clase, así cualquier objeto instanciado a partir de una de las clases derivadas de esta podrá pedir ser desplegado. El área gráfica deberá tener operaciones que permitan desplegar los diversos tipos de objetos, así, podrá desplegar un volúmen, una imagen, un histograma, etc.. . También tendrá posibilidades de realizar algunas 'primitivas' gráficas, cómo dibujo de lineas y puntos por ejemplo.

A continuación se muestra un diagrama de clases donde se detalla lo descrito anteriormente.

22

_ _ - - - - _

4.4 Refinamientos de la colección de datos.

En el punto anterior hemos hecho énfasis en el mejoramiento de las abstracciones concernientes al manejo de la interacción y de los procesos. Esto consituye dos de las partes principales dentro del diagrama de clases de análisis, sin embargo, también debemos refinar la parte de la colección de datos, que es en cierta forma el hucleo' de la aplicación.

23

Veamos de nuevo cómo se definió esta parte en el diagrama de clases de análisis.

I f

I f i

f f

f f

/ i' i f

J f

En esta propuesta inicial se reagrupo el Stack, el Volumen y el Histograma, dado que todos son en el fondo un arreglo de distintos tipos de dato, se penso que podian ser todos instancias de la clase CColeccionDeDatos, sin embargo, durante la etapa de diseño se cuestiono nuevamente la necesidad de que el Histograma formase parte de las clases derivadas de la colección de datos. Varios puntos revelan que esto no es totalmente necesario. En particular se pueden mencionar:

No es necesario cargar y guardar datos de un histograma ya que estos se calculan rápidamente y dependen de una imagen. El guardar el histograma y la imagen juntos no es algo muy práctico. No es necesario que las imagenes siempre tengan un histograma calculado, pues esto podría utilizar memoria inutilmente. Normalmente sólo se van a emplear histogramas para imágenes con tonos de gris, por lo que el tipo

de dato que compone su arreglo será más probablemente alguno de los tipos 'nativos' (int, char), esto permite hacer objetos de un tamaño menor y no es tan necesario instanciar la clase abstracta con uno de estos tipos de datos, es más sencillo implementar un arreglo 'común y corriente'.

El histograma se puede entonces simplificar y ya no es necesario instanciar esta clase a partir de la C ColecciónDeDatos. Esto no invalida de ninguna manera la razón de existir de la CColeccionDeDatos. Simplemente será empleada para clases que necesiten las caracteristicas que provee. El volúmen y el stack serán por el momento las únicas clases que se instancien a partir de ella, pero existe la posibilidad de crear otras clases posteriormente.

Entonces el nuevo diagrama de clases de la colección de datos es el siguiente:

r--- ,Dato I I J

C- CoieccbnDeDafosT

/ /--

/

\

\ \ \ \ \

C-Imagen ? 2 5 C-Histograma

C Punto

En este detalle podemos ver como quedaron las diversas clases. Es importante notar que las clases C Punto y CImagen heredan a partir de C-DatoDeColeccion. La razón por la cual sucede esto es por qie la C-ColecciónDeDatos delega varias de las responsabilidades a los objetos que componen su

25

arreglo y estas pueden no ser forzosamente necesarias. El heredar de C-DatoDeColeccion garantiza que al ser llamadas estas funciones por la clase instanciada no se producira un error debido a que no estan declaradas. Las operaciones que declara C-ColeccionDeDatos son las siguientes:

ReservaMemoriaO LiberaMemoriaO CargaDatosO GuardaDatosO

4.4.1 El arreglo como lista ligada.

En la parte de análisis de la aplicación se menciono que la Colección de datos era una clase que representaba un arreglo de datos de tipo diverso. No se menciono nada sobre la forma en que se almacenaban estos datos. Una solución al problema fue emplear una estructura de datos para almacenar los elementos. Así la colección recibe los mensajes tales como las peticiones de datos, y los pasa a la estructura de datos que se encarga de hacer la búsqueda. La ventaja de esto es que la estructura de datos queda encapsulada y puede ser cambiada sin repercusiones para el resto de la aplicación. Otra ventaja es que la estructura puede llevar el control sobre la memoria que se esta empleando y se evita perder memoria que no se libere.

La estructura elegida fue una lista ligada pues la colección representa en cierta forma un arreglo 'linear' de datos. La colección tiene entonces una lista ligada agregada. A continuación se muestra la colección junto con la lista ligada.

riato n I

4.5 Diagramas de secuencia.

Hemos terminado de estudiar los refinamientos que se hicieron sobre las abstracciones. En esta etapa, las clases tienen ya definidas las operaciones que en la parte de análisis eran sólo 'frases'. A continuación se presentan nuevos diagramas de secuencia en donde se aplican las modificaciones que acabamos de realizar.

Para empezar, se muestra el diagrama de inicio de la aplicación.

26

A ~ G Z Z I ~ I ~ 1 ~ 1 ~ 1 De lrna genes P ri n ci I) a I StackDelrna enes

: Usuario

niciaAplicacion0

StackDelrnagenesO '

-Aplica ción0

I

Abreventanao

ActivlVent.n.óU 7 "Recibe interacción por el usuario"

IP

En este diagrama podemos ver como la aplicación crea las instancias del manejador, del procesador y del stack con el que se va a trabajar. Enseguida, la aplicación le pide al manejador que se cree la ventana correspondiente, y esta entra en su ciclo de espera. Cuando la aplicación termina, se ve como se destruyen los objetos.

Diagrama Cargar Datos:

27

P

CambiaNombreDelArchivoO / .

: Cierraventanao

Abreventanao j AdivaVe ntanao

o Seieccionaventanaoi

"Pide pa rá m etrod'

,"Inserta parámetrod' :

U' /

: ConfiguraFormatoO . u- ' Cierraventanao >-

En este diagrama vemos como la instancia de alguna de las clases derivadas de la colección, al recibir el mensaje de carga de datos, pide una ventana (recordemos que la ColeccionDeDatos tiene una referencia al manejador), a través de la cual obtendrá el nombre del archivo de donde se cargaran los datos. No vamos a presentar el diagrama de Guardar Datos, ya que se trata esencialmente de la misma situación.

Diagrama: Aplicación de un proceso.

28

En este diagrama podemos ver cómo se selecciona el algoritmo, se instancia el proceso, se pide una ventana para llenar los parámetros y finalmente se aplica.

4.6 Diagrama de clases.

De la misma manera que se cerro el ciclo de análisis, se termino la etapa de diseño con los diagramas de secuencia y con un diagrama de clases más completo que se presenta a continuación.

29

~~~ ~

4.7 Conclusión de la etapa de diseño.

Al terminar la etapa de diseño, hemos refinado suficientemente el diagrama de clases y las relaciones entre estas. También conocemos mejor los mensajes que se intercambian los objetos para realizar las tareas necesarias. Durante la siguiente etapa nuevamente entraremos en otro ciclo para mejorar algunos detalles que se hacen visibles sólo al momento de la implementación.

5 .- L a implementación. -

La etapa que normalmente se espera más dentro de la creación de una aplicación es la de la implementación, sin embargo es importante haber llegado a un buen avance de las partes que la preceden para evitar 'improvisar' el código. Normalmente no se debe crear una aplicación desde cero, se puede utilizar alguna herramienta que escriba el código a partir de los diagramas de clases (por ej. Rose), o bien un entorno de desarrollo que cree un 'esqueleto' de la aplicación (ej. Visual C++). Por desgracia en nuestro caso en particular, disponiamos solamente de una herramienta para la creación de ventanas llamada fdesign.

Durante la etapa de implementación, se hizo énfasis en los siguientes puntos principalmente:

30

Crear clases en la 'punta' de la jerarquia para permitir paso de referencias a los objetos. Encapsular la libreria de interfase con el usuario dentro del modelo. Optimizar el despliegue de gráficos. Permitir el uso de multiples formatos de imágenes. Aprovechar la estructura de datos en los lugares donde se requerian arreglos. Permitir que la configuración del stack organizara el despliegue de este.

Vamos a ver con detenimiento cada uno de los puntos mencionados y explicar cómo se llegó a una solución.

5.1 Las clases C Ob-ietoInteractivo y C Ob-ietoModificable.

En la etapa de diseño definimos la estructura de clases que se acerca a lo que podemos llamar 'definitivo'. Sin embargo, existen algunos problemas que surgen en el momento de la implementación a nivel del paso de referencias a objetos. Si recordamos el funcionamiento del manejador de ventanas, este es capaz de instanciar ventanas para diversos tipos de clientes, pero a nivel de código, esto puede presentar alguna dificultad. Para resolverla, se decidió crear una nueva clase, de la cual hereda cualquier objeto que pretenda hacer uso del manejador, su nombre es C-ObjetoInteractivo. De esta forma, el manejador recibe una referencia a C-ObjetoInteractivo y así, cualquier objeto que haya heredado de esta clase entra en la categoría. Aprovechando esta clase, se le incluyó una referencia a C-Ventana, de igual forma que lo mencionado anteriormente, cualquier ventana puede entrar dentro de esta categoría, adelante veremos cuando se utiliza esta referencia. Finalmente, la clase tiene tambien un identificador a la ventana que se tiene asociada. A continuación se muestra la declaración de esta clase.

class C-ObjetoInteractivo t protected: id - vent Idventana; C - Ventana *VentanaAsociada;

id - vent LeeIdVentanaO {return Idventana;} void AsociaVentana(C-Ventana *ApVent) {VentanaAsociada=ApVent;}

public:

1;

Esta misma estrategia se empleo para el paso de referencias dentro del procesador de imágenes, para ello se emplea otra clase llamada C-ObjetoModificable. Hay que notar que un objeto interactivo no es necesariamente modificable por algun proceso y viceversa. El objeto modificable sólo contiene un identificador de la clase cliente.

5.2 Encapsulando la interfase de ventanas.

Un problema dificil con el cual nos topamos durante la implementación fue el de encapsular la librería de interfase de ventanas dentro del modelo. A continuación, se muestra un diagrama que muestra el procedimiento a seguir para obtener interacción por parte del usuario, se trata del diagrama de secuencia de la interacción con la ventana principal.

31

m VentanaAotual : C I VentanaPrinciDai 1

8 Interaccion ... s

las acciones más impotiantesya que hay comunicacion co n otros objetos.

C-VentanaPrincipal() :

. AsignaAreaGrafica(C-AreaGrafica) i c .. .- . .- DespachaCallbadQ)

-

/ I

Menupesplie g u e(int)

U' /

Men+Procesa<int)

u- u-

/

Me nb Arch iuo(int) J

: Fin de interaccioh ... : -C-Apiicación() i

U? CanuasHandlei()

I i

I

i

On-MENU-DE SPLIE GUE( )

On-M ENU-PRO CESAR( )

On-MENUARCHIVO()

RefrescaAreaGrafica( )

-

I I I I

Callbackse encarga de llamar a las funciones atribuidas a cada objeto de la intetfaz de usuario. N o s e han detallado todas, sólo aquellas que a su vez l laman alguna función de I I

En este diagrama podemos ver cuales son los pasos que debe seguir un objeto que desea interacción por parte del usuario. Primero, debe llamar a la función SeleccionaVentana() del manejador, le envia dos parámetros, un identificador de la ventana que desea abrir (definidos en el archivo General.h), y una referencia al objeto que va a ser el cliente, en este caso se trata de si mismo, por eso se envia 'this' (puede ser una referencia a otro objeto que a si mismo).

Del lado del manejador, se procesan las peticiones de la manera siguiente:

bool CManejadorDeVentanas::SeleccionaVentana(id-vent seleccion,C-ObjetoInteractivo* Cliente) {

C-Ventana *VentanaActual; bool RetVal; despues de que se cierra.

switch(se1eccion) I

/ / El valor que regresa la ventana

case IDV - APLICACION: CAplicacion::Inicia()

/ / LLamada realizada por

VentanaActual=new C-VentanaPrincipal(C1iente);

32

Cliente->AsociaVentana(VentanaActual) ; break;

case IDV-SEL-ALGORITMO: CAplicacion::Menu-Procesar()

ventanas)

/ / Asocia la ventana actual

/ / LLamada realizada por

(<- Aqui van otras líneas de otras

VentanaActual->Abreventana(); / / Abre la ventana. VentanaActual->Activaventana(); / / Entra al ciclo de espera de eventos RetVai=VentanaActual->Cierraventana(); / / Cierra la ventana. delete VentanaActual; / / Libera la memoria . . . return RetVal;

Como vemos, el manejador instancia la ventana, enseguida la asocia al objeto cliente (esto esta garantizado pues se deriva de C-ObjetoInteractivo). El manejador invoca la operación Abreventanao en donde se realizan inicializaciones y después invoca Activaventanao que básicamente es la entrada al ciclo de espera de eventos. Enseguida se cierra la ventana, se destruye el objeto y se regresa un valor que da la ventana.

Regresando al diagrama de secuencia, vemos que al recibir interacción, la ventana llama a ciertas operaciones de la aplicación. El control esta entonces del lado de la ventana, sin embargo puede aparecer la necesidad de que durante una de las operaciones del objeto cliente se necesite realizar alguna notificación al usuario, eso se realiza gracias a que el cliente tiene una referencia de su ventana, y a través de ella puede acceder a las operaciones públicas de esta.

Todas las ventanas tienen entonces que tener como mínimo las operaciones que se declaran en Ventana.h por ello, se derivan todos de esta clase, garantizando que el manejador no intentará llamar operaciones inexistentes.

class C - Ventana I protected: bool RetVal; / / Valor de eventual retorno void Inicializa0 { ; } / / Posibles Inicializaciones.

public : C - Ventana ( ) {RetVal=TRUE; 1

virtual bool AbreVentana()=O; / / Abre una ventana, recibe referencia de la clase

virtual bool ActivaVentana()=O; / / Recibe entrada de la ventana virtual bool CierraVentana()=O; / / Cierra la ventana

que invoco al manejador!

33

La ventaja de esta solución es que se pueden emplear diversas librerias para implementar la interacción con el usuario, ya que casi siempre se sigue una secuencia similar de pasos a seguir para mostrar una ventana en los diversas librerías de interfases que existen.

También mostramos a continuación cómo se implementa del lado de una ventana el llamado a las operaciones de su cliente. En el caso de la librería que empleamos, hubo que implementar un despachador de mensajes, pero esto puede no ser necesario para otras librerias de GUI (interfase gráfica con el usuario).

void C-Ventanaprincipal: :Despacha-Callback(1ong param) I

switch (data) {

case(CB-MENUARCHIVO): Cliente->OnMenuArchivo(); break;

case(CB - MENU - PROCESAR): Cliente->OnMenuProcesar(); break;

case(CBMENU-DESPLIEGUE): Cliente->OnMenuDespliegue(); break;

case (CB - MENU-AYUDA) : Cliente->OnMenuAyuda(); break;

case (CB - BOTON - ARRIBA) : case (CB - BOTON - ABAJO) : case (CB - BOTON-IZQ) : case (CB-BOTON-DER) : Cliente->PonCursor(COLOR-MARCO); Cliente->MueveCursor(para); Cliente->PonCursor(COLOR-CURSOR); break;

case(CB - BOTON - ZOOM) : Cliente->OnBotonZoom(); break;

Nota: En xforms, no se puede tener acceso directamente a los atributos de la clase desde una de sus operaciones si esta es llamada como 'callback' pues en ese caso hay que declarar a esta operación de tipo

34

'static'. Es por ello que el archivo manejador.cpp difiere un poco del código mostrado arriba. Posteriormente se tratará este punto con mayor profundidad.

En todas las ventanas se siguen pasos similares a los de la ventana principal, con excepción de los diálogos que no necesitan las funciones de Abreventanao y Cierraventana(), en los diálogos estas están implementadas como funciones vacías.

5.3 Meioras en el área gráfica.

El área gráfica es fundamental para esta aplicación, de ahí que sea necesario un buen desempeño de su parte. Un punto importante que discutiremos es que la clase implementa operaciones que permiten el despliegue de los diversos objetos. Esto puede parecer erroneo pues normalmente uno esperaría que los objetos instanciados a partir de los derivados de CColeccionDeDatos y algunos otros pudieran 'saber' como desplegarse, dado que tienen una operación llamada DespliegaDatos().

Sin embargo, debemos recordar que uno de los objetivos del diseño de la aplicación fue de separar las partes con riesgo de ser dependientes de la plataforma en clases aparte. De esta manera, ninguna de las clases centrales al modelo contiene comandos que no sean estándares al lenguaje de programación estándar. Si por ejemplo un StackDeImagenes incorpora comandos gráficos dentro de su operación DespliegaDatos(), se pierde la posibilidad de portar sin modificaciones los archivos que definen esta clase a otra plataforma.

Otra posibilidad sería que la clase tuviera todas las primitivas gráficas encapsuladas en sus operaciones, y los objetos centrales a la aplicación llamarán a estas primitivas para desplegarse. Desgraciadamente, esto traería otro problema pues los objetos también tendrían que encargarse de que el despliegue se este realizando de manera correcta (inicializar el despliegue, colores, etc. ..).

Es por ello que se decidió que el área gráfica no encapsulara todas las primitivas más básicas de la graficación, sino que pudiera desplegar también los objetos que se tienen definidos, aunque para acceder a los datos de los objetos que se quieren desplegar tendria que obtenerlos a través de sus operaciones. Esto permite que el área gráfica se encargue de 'administrar' el despliegue para que se realice de manera correcta y no se rompen las reglas de encapsulamiento.

Otro punto importante tiene que ver con el hecho de que el área gráfica debe ser capaz de aceptar diversas librerías de primitivas gráficas, en nuestro caso empleamos OpenGL, pero en teoría se podria emplear alguna otra (DirectX por ejemplo). Existen algunas funciones que estan implementadas en una librería y que en otra podrian no estarlo, por ejemplo, el uso de doble buffer para el dibujo. El área gráfica se encarga de hacer 'transparente' esto para los objetos de la aplicación, y ellos sólo tienen que solicitar el tipo de despliegue que se desee (2D/3D) y lo demás se realiza de manera automática.

Algunas dificultades aparecierón por el uso de OpenGL, serán discutidas más adelante.

5.4 Meioras en la clase C Imagen.

Una pregunta puede surgir al estudiar el diseño de la aplicación y es ¿ Por qué la clase C-Imagen no se deriva de CColeccionDeDatos, si se trata básicamente de un arreglo ? La respuesta es que C-Imagen se usa para instanciar la clase C-StackDeImagenes y si se derivara de la colección, tendría que existir una clase más básica para poder instanciar a C-Imagen y sería algo inutil.

En vez de eso, C-Imagen encapsula un arreglo de elementos de tamaño variable. Sin embargo esto no

35

se realizó con una clase parametrizada, pues C-Imagen debe ser capaz de manejar tipos de dato no estándares como por ejemplo 3 bytes por dato. La solución propuesta fue que el arreglo de elementos fuera de tipo 'void *'. Este tipo de datos prefiere evitarse normalmente, pero las ventajas que proporciono en nuestro caso justifico claramente su utilización.

Un problema que surge con tipos de dato no estándares es que no se puede declarar una funcion LeeDato() que regrese un void *. Lo que se hizo es que para tipos de dato menores a 3 bytes, se emplea la función LeeDato que regresa un entero (tipo de dato en el cuál caben 1 o 2 bytes sin problema). Para 3 bytes o más, se emplea la funcion LeeDatoComoArreglo() que regresa un arreglo de yt bytes. Es así como se puede manejar cualquier tipo de dato por la imágen. Esto permite que en el futuro se puedan procesar otros tipos de información, para ejemplificarlo, la aplicación permite desplegar archivos de 3 bytes de información por pixel (1 byte por componente R, G y B) y que tiene como resultado el despliegue de imágenes a color.

5.5 Meioras Y otros usos para la lista ligada.

El uso de una estructura de datos para manejar el arreglo de datos de cualquier tipo dentro de la colección de datos es una idea útil. La clase C ListaLiaada declara una lista de referencias a objetos. La razón por la cual la lista no contiene las instancias es por que la lista se pretende emplear en algunos momentos como un 'vehiculo' para enviar parámetros. Es por ello que no debe contener las instancias de los objetos, de lo contrario, estos serian destruidos junto con la lista. De ahí que la responsabilidad de creación de los objetos no es de la lista ligada, sin embargo, se le puede pedir que destruya a los objetos que referencia antes de destruirse.

Dadas las caracteristicas de la lista ligada, se le encontraron otros usos dentro de la aplicación, por ejemplo dentro de la aplicación, se tenía un stack original y uno de trabajo, pero que sucede si se aplican varios procesos, donde se guarda la referencia a los stacks resultantes de los procesos? Una solución elegante fue emplear una lista ligada para contener todos los Stacks con los que trabaja la aplicación, esto permite tener una cantidad variable sin limite de tamaño. Otro empleo que se le dio a la lista ligada fue para la clase C Volumen pues un volumen no es sólo una colección de puntos en el espacio tridimensional, sino que cada uno de esos puntos tiene una normal que define su orientación respecto a las fuentes de luz, y un color. Hubiera sido posible crear una clase derivada de C-ColeccionDeDatos para el manejo de las normales, pero las normales no pueden 'existir' como objeto individual de un volúmen, y no hay posibilidad de desplegarlas. Por todo ello, mejor se emplearon dos listas ligadas dentro de C Volumen, una de puntos para contener las normales y una de colores. Es interesante notar que un volumen no necesita forzosamente que estas listas contengan datos para poder desplegarse satisfactoriamente.

Otros lugares donde se emplean listas ligadas es para los algoritmos. Por ejemplo, la aplicación necesita pasar dos stacks distintos al proceso de reconstrucción, el stack original (niveles de gris) y el segmentado. Estos se introducen a una lista ligada que se pasa al proceso. A partir de los stacks que recibe, el proceso de reconstrucción crea un nuevo stack, el de contornos, y lo añade a la lista que se le envio, así al terminar el proceso, la aplicación tiene acceso al nuevo stack.

Para poder emplear listas ligadas de las diversas clases, se han definido varias instancias de la clase C-ListaLigada, en particular podemos mencionar:

C-ListaColores definida en Co1or.h C-Listapuntos definida en Punt0.h C-Listastacks definida en Stack.h

36

Es posible crear otras listas ligadas si es necesario.

Un problema que surgio al emplear listas ligadas de gran tamaño fue que el acceso a sus datos se volvió lento. Esto es debido a que la lista se recorre desde el inicio cuando se le pide uno de sus elementos. Para mejorar el tiempo de acceso, se decidió que la lista mantuviera un arreglo de apuntadores a sus nodos, así no se tiene que recorrer toda la lista para llegar a un nodo dado. Para ello se incluyó la operación ActualizaTablaO. Como su nombre lo indica, esta función actualiza la tabla de apuntadores a los nodos y cambia una bandera de la lista que indica que la lista esta actualizada. De esta forma, cuando se invoca al método LeeDato(DatoNum), la lista utiliza automáticamente su tabla. La actualización se invalida también de manera automática cuando se inserta o se borra un elemento a la lista. AI copiarse la lista, se actualiza la tabla de la lista fuente para optimizar la velocidad.

El resultado de utilizar una lista fue una mejora en todos los aspectos de la aplicación.

5.6 La configuración del stack Y sus usos.

Hemos explicado ampliamente los puntos importantes que conciernen al stack de imágenes. Sin embargo, los requerimientos de la aplicación pedian la posibilidad de interactuar con el despliegue, por ejemplo, de ver detalladamente una imágen o de variar la manera en que se despliega un stack. Esto es claramente algo fuera de las responsabilidades del stack, sin embargo, también está fuera de las responsabilidades del área gráfica, que se limita a proveer a lo máximo un área gráfica con ciertas carácteristicas. Si por ejemplo queremos desplegar todo el stack, o solo una parte, quien debe responsabilizarse de realizar la distribución de las imágenes en la pantalla ?

La solución empleada fue la de crear una nueva abstracción, la llamamos C StackConfiq y está asociada al C-StackDeImagenes. Si observamos sus atributos, encontramos que contiene todo lo necesario para poder desplegar un stack en un área rectangular de la pantalla, desde la escala a la cual se despliegan las imágenes, hasta la franja que existe de espacio entre imágenes. También contiene una referencia al área gráfica en la que se despliega el stack. Existe una redundancia al tener una referencia al área gráfica en el Stack y en su configuración. Esto se debe a que la configuración a pesar de tener una referencia a un stack, no puede conocer el área gráfica en que este se despliega, pues esto no es parte de las operaciones que tiene la colección. Por ello el Stack le dá a conocer su área gráfica a su configuración. La utilidad de tener esto es que ante un cambio de tamaño del área de despliegue, la configuración puede actualizar la distribución de las imágenes en la pantalla, así como el tamaño máximo de despliegue.

Otro uso de la configuración apareció posteriormente con la posibilidad de ver una ampliación de una imágen en la pantalla principal, esto se logra simplemente ajustando la escala actual a la máxima posible, pero esto lo realiza automáticamente la configuración, y es posible despues regresar al tamaño original.

La configuración puede ser utilizada para desplegar una sóla imagen en la pantalla, se debe crear un stack de una imágen, y esto automáticamente le creará una configuración. Esto es útil para mostrar imágenes centradas o escaladas. El uso de la configuración del stack permitió tener mucha flexibilidad en el despliegue de las imágenes dentro de la aplicación.

5.7 Diagramas de secuencia.

Para finalizar la etapa de implementación, mostraremos ,al igual que en las demás etapas, los

37

diagramas de secuencia que muestran las modificaciones que presentamos en los puntos anteriores.

Diagrama: Inicio de aplicación.

inejadotü eVeniana5

rocesadorDelm agenes1

: -StackD elm agenes( ) i 7

AsignaManejador( )

:iaJ

' VentanaP rincipal( ) - ikntana(id-vent, Reft ______51

Selec

iaVentana(C -Veniani

&reVeniana( ) A inicializa( )

I LeeStacki )legado( )

&sign( I- eaGrafica(C -#reaGrafqa) .

ActivaVentana( ) RelrescahaGrafica( : P .

?satiint)

D espliegaDaios( 1 u; Dibuja!magen(int, int, int, C -1

3' allback[ I Esto se mue stra coi detalle en lo dem as diagram as. e---

L

MenuP

iw(int)

. Menu, ,

MenuDi egue(int) . C ierra%ntana( )

:IA INICIO DE APLICAC~ION )IAGRPMADE SECL

38

Diagrama: Carga de un stack.

I

5 eieccionaventana$u-vent, Hetemnci a-I) 1

C Dialoq#rchko(C * Obieiolnier

ú' U *-

' Abi.eVeitaiia() ! _ .

i Activaventana()

CombiaNoml I :I<

contigura()

! ! ! ! tk0 *. ¡ni)

\ ! Arc hiv o(c hor *)

I .. ! jc Venianaconmac # j

1'1- I . I

39

Diagrama: Aplicación del algoritmo de segmentación por umbral.

Leektd: argadd.21

SeleccionaM nianq

I

vent Reíerencia-D. c

ibiaSeIeccionAlgori1

c o

is ertaNod C-Dato') i.

r

SeleccbnaProces o()

Selecc&aMntan&id-ven

C-Vent

aLknianá 1

ikniang )

BcCegU mbra[R efere!

3

Diagrama: Configuración de parámetros del despliegue:

40

a

Selec cionaVentana(id-\ it, Referencia-I)

C-VentanaConfStackD I - Abreventana( ) - ActivaVentana( )

.--

Cierraventana( )

’( L S t a c kconfig( r inicializa( )

I

Copia(C-Sac kConl /

Despac hac allbac k

a Cam bialm agenlnici;

.-- Cam biaEscala()

C am b i al nte ns id ad(;

\ /

6

Copia(C-

LeeConfiguracion( )

Estas son las modificaciones que se realizan

configuración.

:ac kconfig) F 1

Diagrama: Interacción con la ventana principal.

41

A I - VentanaAotual : C 1 1 I ADlicacion Mane'adorDeV ntanas

Mpne iador jC Aplicacion : C : usuario " I

: seleccionaventana u

j Interaccion ...

M

: Fin de inteiac

i -CApl icac ión() 1

impottantesya que c

p P L i CACI ON.this)

C-ventanaPrincipai()

Abreventana()

CanvasHandleO

i l l

Asi g n a Are a G raf i ca(C-Are a G iaf i ca) >

sp l ieg ue(int)

Despacha Call b a y )

On-M EN U-D E S P LI E G U E( )

On-M ENU-PRO CESAR( )

'rooesar(int)

On-MENU-ARCHIVO()

4rchivo(int)

RefrescaAieaGrafica()

Callbad<se encarga de llamar a las funciones atribuidas a cada objeto de la interfrz de usuario. N o s e han detallado todas, sólo aquellas que a su vez l laman alguna función de 1, a p I i ca ci ó n

Diagrama: Despliegue del stack actual.

42

s elec cion aVentana( id-'

Abreventana( ) L

it, Referencia-I)

C-VentanaConfStackC

Activaventana( )

Cierraventana( )

)CStackConfig( )

'u inicializa( )

I ; LeeConfiguracion( ) j

C op ia ( C-Sta c kC o nf i$ )

DespachaCallbaclQ i) 'u

Cam bialntensidad( : XI C opia(C-dac kconfig) \ I /

5.8 Diagramas de clases.

A continuación se muestran diversos diagramas de clases definitivos para la versión que se entregó.

Diagrama de clases global:

43

. ..

li

i-

‘\

/’

\ ‘\ k-9 O O

- /

uso de las clases de ventanas:

C-VentanaPrincipal : Ventana principal de la aplicación, donde se muestran los stacks. C-VentanaSelAlgoritmo : Ventana para seleccionar el algoritmo a aplicar. C-VentanaConfStack : Ventana para configurar el stack al cargarlo. C-VentanaConfStackDisp: Ventana para configurar el despliegue del stack. C-VentanaConfSegUmbral: Ventana para aplicar el algoritmo de segmentación por umbral. C-VentanaReconstruccion: Ventana para aplicar el algoritmo de reconstrucción. C - DialogoArchivo: Dialogo para explorar el directorio y teclear un nombre de archivo.

Diagrama de clases : Detalle de relaciones entre la aplicación y la ventana principal.

Diagrama de clases : Detalle del proceso de segmentación por umbral.

45

Diagrama de clases: Detalle de proceso de reconstrucción.

46

c Y

\

5.8.1.- Detalles de las clases.

Algunas clases se pueden ver con mayor detalle:

C-Aplicación C-AreaGrafica C-ColeccionDeDatos C Histograma Climagen CListaLigada C-Stackconfig C-StackDeImagenes C-Ventana C-VentanaPrincipal

diagrama - cabecera diagrama - cabecera diagrama - cabecera diagrama - cabecera diagrama - cabecera diagrama - cabecera diaprama - cabecera diagrama - cabecera diagrama - cabecera diagrama - cabecera

47

C-Volumen diagrama - cabecera

5.9 Conclusión.

Hemos llegado finalmente a la etapa terminal de la creación de una aplicación. En este capitulo mostramos las soluciones que se emplearon para llevar a cabo de manera exitosa la propuesta del diseño. El resultado 'fisico' fue exitoso, la aplicación funciona correctamente, y sobre todo es sencilla de modificar y de extender. Posteriormente se muestran algunas imágenes del resultado.

6.- Aspectos Técnicos.

En esta sección se discuten algunos aspectos técnicos muy específicos al desarrollo de la aplicación con la librería xforms, opengl, y la herramienta 'make'.

6.1 Recuperación del apuntador 'this' dentro de xforms.

La librería xforms es una colección de objetos gráficos que permiten la interacción con el usuario. Está escrita en lenguaje 'C' y posee una herramienta que permite la creación de ventanas de manera 'interactiva' y posteriormente la escritura del código para esas ventanas de manera automática. Sin embargo, nosotros enfrentamos un problema cuya solución no fue muy evidente, el problema fue ¿cómo encapsular la librería en C dentro del modelo?.

Cuando fdesign escribe el código correspondiente a una forma o ventana, escribe una cabecera (archivo .h) y una parte del código del programa (archivo .c). A continuación se muestra un ejemplo de estos archivos.

Cabecera:

# i f n d e f FD - ventana-h - P r i n c i p a l # d e f i n e FD - ventana _ - h P r i n c i p a l / * Header f i l e g e n e r a t e d w i t h f d e s i g n . * /

/ * * * * Forms and O b j e c t s * * * * /

typedef s t r u c t {

FL - FORM *ventana; FL - OBJECT *MENU - ARCHIVO; FL - OBJECT *MENU - PROCESAR; FL - OBJECT *MENU - DESPLIEGUE; FL - OBJECT *canvas; FL - OBJECT *BOTON - SALIR; FL - OBJECT *MENU - AYUDA; FL - OBJECT *CAMPO - TEXTO; FL - OBJECT *BOTON - I Z Q ;

FL OBJECT *BOTON ABAJO; - -

48

FL - OBJECT *BOTON - ARRIBA; FL - OBJECT *BOTON - DER; FL - OBJECT *CAMPO - STATUS; FL OBJECT *CAMPO NUM IMAGEN; FL - OBJECT *BOTON - ZOOM; void *vdata; long ldata;

- - -

} FD-ventana - Principal;

#endif / * FD ventana h * / - - -

Cuerpo del programa (se muestra sólo una parte):

/ * Form definition file generated with fdesign. * /

#include "forms. h" #include "form - - V principal. h"

FD - ventana *create-form - ventana(void) I FL - OBJECT *obj; FD - ventana *fdui = (FD - ventana * ) fl - calloc(1, sizeof(FD-ventana));

fdui->ventana = fl - bgn-form(FL - - NO BOX, 1220, 940); obj = fl - - add box(FL - - UP BOX,O,O,l220,940,'"); obj = fl - - add frame (FL - ENGRAVED-FRAME, 10,10,260,80, ;

fdui->MENU - ARCHIVO = obj =

fl - - add menu(FL - PULLDOWN - MENU, 50,900,110,30, "Archivo") ; fl - - set object - boxtype (obj, FL - FRAME - BOX) ; fl - - set object - lstyle(obj,FL - BOLD - STYLE); fl - - set object - callback(obj,Despacha - Callback,CB - MENU - ARCHIVO);

fdui->canvas = obj = fl - - add canvas (FL - NORMAL - CANVAS, 10,100,1200,780, " " ) ;

fdui->MENU - AYUDA = obj = fl - - add menu(FL - PULLDOWN - MENU, 360,900,100,30, "Ayuda") ; fl - - set object - boxtype (obj, FL - FRAME - BOX) ; fl - - set object - lstyle(obj,FL - BOLD - STYLE); fl - - set object - callback(obj,Despacha - Callback,CB - MENU - AYUDA);

fdui->BOTON - ZOOM = obj = fl - - add button(FL - PUSH - BUTTON, 150,50,110,30, "Zoom") ; fl - - set object - lsize(obj,FL - MEDIUM - SIZE); fl - - set object - lstyle(obj,FL - BOLD - STYLE); fl - - set object - callback(obj,Despacha - Callback,CB - BOTON - ZOOM);

fl end form(); - -

return fdui; I

49

¿ Cómo realiza la interacción con el usuario xforms ? La librería provee los objetos gráficos, y permite que se asocie una función a cada uno de ellos, esta se denomina callback y se asocia por medio de la función fl-set-object-callback().

Debemos prestar atencion a la función fl-set-object-callback(). En ella se envian tres parámetros, el primero es una referencia al objeto, el segundo es un apuntador a la función que se debe llamar en caso de recibir interacción y el tercero es un parámetro.

El problema que aparece es que el callback NO PUEDE SER CUALQUIER FUNCION! Si intentamos poner alguna de las operaciones de una clase dentro del fl-set-object-callback, la operación será llamada de manera exitosa, sin embargo una vez ahí ocurriran problemas. El primero es que el parámetro que se envía parece tener un valor totalmente distinto al que pensabamos enviar. El segundo es que ya no tenemos acceso a los atributos de la clase de manera normal.

Este problema parece originarse debido a que la librería esta creada en lenguaje IC'. La solución es que la función que se llama como callback debe ser declarada como 'static' en la declaración de la clase. Sin embargo, si declaramos una función de tipo 'static', una vez que llegamos a ella, no podemos emplear el apuntador 'this', ni tener acceso a los atributos del objeto, sin embargo, el parámetro enviado se recibe correctamente. Parece no haber solución al problema, pero lo que se debe hacer es 'recuperar el apuntador this'.

'Recuperar el apuntador this', significa que debemos almacenarlo en algun lugar al que se pueda acceder dentro de la función estática. Recordemos que los atributos del objeto no son candidatos para esto pues contienen valores aparentemente erroneos ... el único candidato son los parámetros que se reciben correctamente.

Al leer la documentación de xforms, hemos encontrado que los autores planearon un apuntador 'libre' para que el usuario pudiera emplearlo para cualquier cosa necesaria. Es ese apuntador es el que vamos a emplear.

Así que dentro de la funcion Abreventanao tendremos que 'almacenar' el apuntador, esto es al inicializar la ventana. A continuación se muestra un ejemplo de ello:

Primero, vemos parte del archivo de cabecera de la ventana, donde se declara de manera estática la función DespachaCallback().

#include ' l . . /FORMS/forms. h" #include "FORMS/form _ - V principa1.h"

[ . ..I

#include "Ventana. h" / / De esta hereda

class C-VentanaPrincipa1:public C-Ventana I protected: / / * * * Enums locales. * * * / /

/ / Los Identificadores de Callbacks

50

enum { CB - MENU - ARCHIVO=l,CB - MENU - PROCESAR,CBMENU-DESPLIEGUE,CB - MENU - AYUDA, CB - MENU - RECONSTRUIR,CB - BOTON - ARRIBA,CBBOTON-ABAJ0,CB - BOTON - IZQ, CB - BOTON - DER,CB - BOTON - ZOOM};

C - Aplicacion *Cliente; / / Esta ventana corresponde a la clase C-Aplicacion. C-AreaGrafica Pantalla; / / El area grafica -> Agregacion.

/ / * * * Metodos * * * / /

/ / Heredada

void Inicializa ( ) ; / / Inicializaciones diversas.

/ / x-forms exclusivamente !

FD - ventana-Principal *forma; / / La forma void CanvasHandlerO; / / Manejador del area grafica.

/ / Callbacks . . .

static void Despacha-Callback(n - OBJECT *ob,long d a t a ) ; / / Despachador . . .

void OnMenuDespliegue(); void OnMenuProcesarO; void OnMenuArchivo(); void OnMenuAyuda ( ) ; void OnBotonZoom ( ) ;

/ / Funciones que son llamadas dependiendo / / de las acciones.

/ / Las funciones siguientes no son parte del modelo especificamente sino de / / x-forms!

static int RecibeEvento(FL - OBJECT *ob, Window wintint w,int h, XEvent *xev, void

FD - ventana-Principal *create - form - ventana(void); *Ud) ;

/ / Otras. . .

[ . . .I

public: / / Constructor.

C - VentanaPrincipal(Referencia-I CliRef) {Cliente=(C-Aplicacion *)CliRef;}

/ / Funciones Heredadas

51

boo¡ Abreventana ( ) ; bool Activaventana(); bool Cierraventana();

Enseguida vemos cómo se guarda el apuntador a this (archivo VentanaPrincipal.cpp).

bool C-Ventanaprincipal: :Abreventana()

forma=create-formaventana() ; / / Creacion de la forma forma->ventana->u-vdata = this; / / Salva el this, para que lo usen los callbacks.

Inicializa ( ) ; / / Inicializa pantalla.

fl - show - form(forma->ventana,FL - PLACE - CENTERIFL-FREE_SIZE,FL_FULLBORDER,"principall'); / / Muestra la forma.

DEBUGMSG ("C-Ventanaprincipal: :Abreventana ( ) \n") ;

return TRUE; / / Siempre. I

Ahora se muestra cómo se recupera el apuntador a this en la función DespachaCallback()

void C - VentanaPrincipa1::Despacha - Callback(FL - OBJECT *ob,long data) t

C-Ventanaprincipal *ApTemp; / / Apuntador temporal ApTemp=(C-VentanaPrincipal *)ob->form->u-vdata; / / Rescata el this

if(ApTemp->EsPrimeraVez==TRUE) ApTemp->BorraIntro();

switch (data) t case (CB - MENU - ARCHIVO) : ApTemp->OnMenuArchivo(); break;

case (CB-MENU-PROCESAR) : ApTemp->OnMenuProcesar(); break;

/ / Ahora si; ejecuta callback!

case(CB - MENU-DESPLIEGUE): ApTemp->OnMenuDespliegue();

52

break;

case(CB - MENU - AYUDA) : ApTemp->OnMenuAyuda(); break;

case(CB - BOTON - ARRIBA): case (CB - BOTON - ABAJO) : case(CB - BOTON - IZQ) : case (CB - BOTON - DER) : ApTemp->PonCursor(COLOR-MARCO); ApTemp->MueveCursor(data); ApTemp->PonCursor(COLOR - CURSOR); break;

case (CB - BOTON-ZOOM) : ApTemp->OnBotonZoom(); break;

La única manera de recuperar el 'this' es a partir de una fuente 'confiable' pero ya vimos que los miembros del objeto no lo son, sólo los parámetros pueden emplearse. Podemos apreciar que los callbacks en xforms siempre están definidos de la manera siguiente:

void NombreDelCallback(F'L - OBJECT *ObjetoQueLlama,long Parametro)

No es posible enviar el apuntador a this como parámetro (que es de tipo long), pero el objeto que llama a la función es la vía por la cual se recupera.

Si prestamos atención a la declaración de la clase, podemos notar que DespachaCallback no es l a única función declarada cómo 'static', sino que también existe otra:

static int RecibeEvento(FL - OBJECT *ob, Window winlint w,int h, XEvent *xev, void *Ud) ;

Lo que sucede es que DespachaCallback es la función que se llama cada que se interactua con las ventanas, pero RecibeEvento es un callback que se asocia al área gráfica, y se llama de manera automática cuando el área gráfica recibe algun evento. Es por ello que RecibeEvento se tiene que declarar como static, y dentro de ella hay que rescatar el this de manera idéntica.

Cómo vemos estas situaciones que aparecen de 'rescate de this' no facilitan en nada el encapsulamiento de la librería dentro del modelo, pero el resultado con esta aproximación es satisfactorio.

53

6.2 Pasos a seguír para implementar una nueva ventana.

Puesto que el programa puede requerir nuevas formas de interacción con el usuario, a continuación se muestra como agregar una nueva ventana.

Para agregar una nueva ventana a la aplicación, tenemos que seguir una serie de pasos. El primero es crear la forma con fdesign. A continuación se muestra una imagen de fdesign en acción la parte de abajo muestra la ventana aue estamos creando.

Nota: Siempre tendremos que llamar a la forma 'ventana' (esto facilita tomar el código de otra ventana y adaptarlo), a todos los objetos se les tiene que asociar cómo callback la función 'DespachaCallback', también se debe de establecer el parámetro que identifique el objeto dentro de la forma y finalmente se les tiene que poner un nombre. La imagen a continuación muestra cómo se ve la pantalla de configuración de un objeto cuando se le aplican estas modificaciones.

54

Cuando se guarda la forma, fdesign genera dos archivos, el primero es el encabezado que es similar al siguiente:

#ifndef FD-ventana - h- #define FD-ventana-h- / * Header file generated with fdesign. * /

/ * * * * Callback routines * * * * /

extern void DespachaCallback(FL-OBJECT * I long);

/ * * * * Forms and Objects * * * * /

typedef struct {

FL - FORM *ventana; void *vdata; long ldata;

} FD - ventana;

extern FD-ventana * create - form - ventana(void1;

#endif / * FD - ventana-h- * /

Para poder incluir la ventana dentro de nuestra aplicación, se deben hacer algunas modificaciones que se muestran resaltadas a continuación.

#i€nde€ FD-ventana-Deprueba-h- <- (1) #define FD-ventana-Deprueba-h- <- (1) / * Header file generated with fdesign. * /

/ * * * * Callback routines * * * * /

/ / extern void DespachaCallback(FL-OBJECT long); <- (2)

55

/ A * * * Forms and Objects * * * * /

typedef struct {

FL - FORM *ventana; void *vdata; long ldata;

} FD - ventana-DePrueba; <- (3)

/ / extern FD-ventana * create-form - ventana(void); <- ( 4 )

#endif / * FD-ventana - - h * /

(1) Es importante cambiar las lineas del #ifndef sino al incluir otros archivos de cabecera apareceran errores al ya estar definido FD-ventana-h-.

(2) Hay que comentar la(s) declaración(es) de DespachaCallback(), ya que esta función será redeclarada posteriormente como operación dentro de la forma.

(3) También hay que cambiar el nombre de la estructura, ya que siempre se llama FD-ventana al ser escrito el código, hay que ponerle algun extra que la diferencíe.

(4) Finalmente hay que comentar la declaración de create-form-ventana() pues de igual forma de DespachaCallback(), será declarada dentro de la clase correspondiente.

La cabecera esta lista para ser incluida dentro del archivo en donde se declara la clase correspondiente a la ventana.

El paso siguiente es retomar el código en C que escribe xforms y 'encapsularlo' dentro de la clase.

A continuación se muestra el código escrito por fdesign para el ejemplo mostrado arriba.

/ * Form definition file generated with fdesign. * /

#include "forms. h f f #include "prueba. h"

FD - ventana *create - form - ventana(void) I

FL - OBJECT *obj; FD-ventana *fdui = (FD-ventana * ) fl - calloc(1, sizeof(FD - ventana));

fdui->ventana = fl-bgn-form(FL-NO-BOX, 600, 250); obj = fl - - add box(FL - - UP BOX,0,0,600,250,"ff); obj = fl - - add frame (FL - ENGRAVED-FRAME, 480,20,110,220, " " ) ;

obj = fl - - add button(FL - NORMAL - BUTTON, 490,190,90,40, "Botonl") ; fl - - set object - lsize(obj,FL - MEDIUM - S I Z E ) ; fl - - set object - callback(obj,DespachaCallback,CB - BOTONl);

56

obj obj = fl - - add menu(FL - PULLDOWN~MENU,1O,210,100,30,"Archivof~);

obj = fl - - add canvas (FL - NORMAL - CANVAS, 10,60,460,140, "") ; obj = fl - - add frame (FL - ENGRAVED - FRAME, 10,210,460,30, f'fv) ;

obj = fl - - add button(FL - NORMAL - B U T T O N , 4 9 0 , 1 2 0 , 9 0 , 4 0 , " B o t o n 2 " ) ;

obj

obj = fl - - add text(FL - NORMAL - TEXT,290,20,180,20,"Forma de prueba.

= fl - - add slider (FL - - HOR SLIDER, 20,20,260,20, "If) ;

fl-set-objectboxtype(obj,FL - FLAT - BOX);

fl - - set object - lsize(obj,FL - MEDIUM - SIZE);

fl - - set object - lsize(obj,FL - MEDIUM - SIZE);

fl - - set object - boxtype(obj,FL-DOWN - BOX); fl - - set object - color (obj, FL - WHITE, FL - MCOL) ;

= fl - - add button(FL - NORMAL - BUTTON, 490,50,90,40, "Boton3") ;

fl end form(); - -

If);

return fdui;

Este código tiene que quedar encapsulado dentro de la función:

FD - ventana - DePrueba *create - form - ventana(void);

De la manera siguiente (nuevamente, se resaltan las modificaciones):

FD - ventana-DePrueba *C - VentanaDePrueba::create - form - ventana(void) <- (1) I / / PEGAR LO QUE CREA XFORMS AQUI

FL - OBJECT *obj; FD ventana DePrueba *fdui = <- (2) - -

(FD-ventana-DePrueba * ) fl - calloc(1, sizeof(FD - ventana-DePrueba)); <- (2)

fdui->ventana = fl-bgn-form(FL-NO-BOX, 600, 250); obj = fl - - add box(FL - - UP BOX,0,0,600,250,frf'); obj = fl - - add frame (FL - ENGRAVED - FRAME, 480,20,110,220, 'Iff) ; ob] = fl - - add button(FL - NORMAL - BUTTON, 490,190,90,40,"Botonl") ;

fl - - set object - lsize(obj,FL - MEDIUM - SIZE); fl-set-object - callback(obj,DespachaCallback,CB - BOTON1);

obj = fl - - add slider(FL - - HOR SLIDER,20,20,260,20,""); obj

obj = fl-add-glcanvas (FL - NORMAL - CANVAS, 10,60,460,140, " ' I ) ; <- (3)

obj obj

obj

= fl - - add menu(FL - PULLDOWN - MENU, 10,210,100,30, "Archivo") ; fl-set-object - boxtype(obj,FL - FLAT - BOX);

= fl - - add frame (FL - ENGRAVED - FRAME, 10,210,460,30, " " ) ; = fl - - add button(FL-NORMAL - BUTTON, 490,120,90,40, "Bot0n2~I) ;

= fl - - add button (FL - NORMAL - BUTTON, 490,50,90,40, ffB~t~n3f') ; fl - - set object - lsize(obj,FL-MEDIUM - SIZE);

fl - - set object - lsize(obj,FL-MEDIUM - SIZE);

57

obj = fl - - add text(FL - NORMAL - TEXT,290,20,18Or20,"Forma de prueba..."); fl-set-object-boxtype(obj,FL - DOWN - BOX); f 1 - - set obj ect-color (obj FLWHITE, FL - MCOL) ;

fl end form(); - -

(1) La función tiene que ser definida como parte de la clase FD-ventana-DePrueba.

(2) Hay que cambiar todas las referencias a FD-ventana por FD-ventana-Nombre en donde Nombre es el nombre de la ventana en este caso es 'Deprueba'.

(3) Si se planea emplear OpenGL,el 'canvas' tiene que ser cambiado por 'glcanvasl.

Finalmente esta parte queda lista para ser incorporado dentro del archivo de definición de la clase (.cpp).

Los pasos siguientes son los que hay que seguir para declarar la nueva clase. Supongamos que el cliente de la aplicación es una clase llamada IC-ClientePrueba'.

/ / Este es el archivo VentanaPrincipa1.h

#ifndef VENTANADEPRUEBA - H #define VENTANADEPRUEBA - H

/ / Includes . . .

#include "General. h" <- (1)

#include "forms. h" <- (2)

#include "Ventana. h" <- (3) #include "FORMS/form - _ V prueba.h" <- ( 4 )

#include "ClientePrueba. h" <- (5)

/ / Defines . . .

class C-VentanaDePrueba:public C-Ventana { protected: / / * * * Enums locales. * * * / /

enum {CB-BOTON-SELECCION1=lrCB - BOTON - SELECCIC

/ / * * * Atributos * * * / /

J2}; <-

C - ClientePrueba *Cliente; / / Referencia al cliente <- ( 7 )

58

FD - ventana-DePrueba *forma; <- ( 8 )

/ / * * * Metodos * * * / /

/ / Callbacks

static void Despacha-Callback(FL-OBJECT *ob,long data);// Despachador de mensajes <- (9)

/ / X-forms

FD-ventana-DePrueba *create - form-ventana(void); <- (10)

/ / Otras

void FuncionExtral ( ) ; <- (11) void FuncionExtra20;

public:

<- (12) C - VentanaDePrueba(C-ObjetoInteractivo* CliRef) {Cliente=(C-Clienteprueba *)CliRef;}

/ / Estas son heredadas.

bool Abreventana ( ) ; <- (13) b o o l Activaventana(); bool Cierraventana();

#endif

(1) Siempre incluir el archivo 'Genera1.h' que contiene declaraciones necesarias. (2) Incluir 'f0rms.h' que son los includes de la librería. (3) Incluir 'Ventanah' que es donde se declara la clase de donde heredan todas las ventanas. (4) Incluir el archivo de cabecera creado por fdesign y modificado posteriormente. (5) Incluir el archivo donde se declara la clase cliente. (6) Crear los enums que identifican a los objetos. (7) Tener una referencia al objeto cliente. (8) Tener un apuntador a la forma. Esto es necesario para las funciones de xforms. (9) Declarar la función de despacho de callbacks. (1 0)Declarar la función createform-ventana() que contiene el código que crea la ventana y que genera fdesign. (1 1)Poner funciones extra que sean necesarias. (1 2)El constructor de la ventana simplemente recibe la referencia al cliente y la guarda en la que se declaro en (7). (1 3)Las funciones heredadas a partir de C-Ventana y que detallaremos a continuación.

59

Una vez que se ha declarado la clase correctamente, tememos que pasar a su implementación. Existen algunas hc iónes que serán identicas para todas las ventanas, estas son Abreventanao y Cierraventanao serán como sigue:

bool C - VentanaDePrueba::AbreVentana() I

forma=create-form-ventanao ; forma->ventana->u-vdata = this;

callbacks . . .

/ / Creacion de la forma / / Salva this para uso de

Inicializa ( ) ; / / Inicializa pantalla

fl - show - form(forma->ventana,FL-PLACE - CENTER,FL-FULLBORDER,"Ventana de prueba"); / / Muestra Ventana

return TRUE; 1

bool C - VentanaDePrueba::CierraVentana()

fl - hide - form(forma->ventana); / / Cierra la ventana

return RetVal; 1

El siguiente paso es definir las funciones Activaventanao y DespachaCallback().

bool C-VentanaDePrueba: :Activaventana() I FL - OBJECT "mensaje;

while (TRUE) i mensa j e=f 1-do-f orms ( ) ; if(mensaje==forma->BOTON-APLICAR) { RetVal=TRUE; / / Espera OK. return TRUE;

1

if(mensaje==forma->BOTON-CANCELAR) I RetVal=FALSE; return FALSE;

i

60

Notese que para el ciclo de espera de eventos checamos el objeto recibido mediante su nombre, en despacha callback usaremos los enums definidos en la declaración de la clase.

/ / SIGUEN LAS DEFINICIONES DE LOS CALLBACKS

void C-VentanaDePrueba::Despacha-Callback(FL-OBJECT *ob,long data) {

C - VentanaSelAlgoritmo *ApTemp; ApTemp=(C-VentanaDePrueba *)ob->form->u-vdata; / / Rescata this.

/ / Apuntador temporal

switch (data) { case(CB-BOTON - SELECCION1): ApTemp->FuncionExtral(); break;

case(CB-BOTON-SELECCION2): ApTemp->FuncionExtra2(); break;

Nuestra ventana está lista para ser incluida al programa. Ahora tenemos que poder pedir su instancia. Hay que seguir algunos pasos sencillos.

1 .- Dar un identificador a la ventana dentro del archivo Genera1.h (se muestra sólo la parte importante.)

[ . . . I

/ / Los siguientes son los identificadores de las ventanas (IDV). 1001->1999

#define IDVAPLICACION 1001 / / Ventana principal #define IDV-SEL-ALGORITMO 1004 / / La seleccion del algoritmo #define IDV-CONF-STACK 1005 / / La configuracion del stack #define IDV-CONF-STACK-DISP 1006 / / Configuracion del despliegue principal #define IDV-CONF-SEG-UMBRAL 1007 umbral #define IDV-RECONSTRUCCION 1008 / / Ventana de la reconstruccion. # d e f i n e IDV-PRUEBA 1009 / / Ventana de prueba.

/ / Configuracion del algoritmo seqmentacion por

/ / Los siguientes son identificadores de dialogo (IDD) 2001->2999

#define IDD-CARGAR 2001 / / Dialogo cargar #define IDD-GUARDAR 2002 / / Dialogo guardar

61

2.- Incluir la declaración de la clase dentro del archivo de definición del manejador

/ / Este es el archivo Manejador.cpp que contiene la definicion de la clase / / ManejadorDeVentanas. AQUI HAY CODIGO ESPECIFICO A XFORMS!

#include "Manejador. h" #include "Ventana. h" #include "VentanaPrincipal. h" #include "VentanaSelAlgoritmo. h" #include "DialogoArchivo. h" #include "VentanaConfStack. h" #include "VentanaConfStackDisp. h" #include "VentanaConfSegUmbral. h" #include "VentanaReconstruccion.h" #include "VentanaAcercaDe . h" #include VentanaDePrueba . hff

[...I

3 .- Incluir también el la operación SeleccionaVentana() el código que se encargue de instanciar esta ventana.

case IDV - RECONSTRUCCION: VentanaActual=new C-VentanaReconstruccion(C1iente); Cliente->AsociaVentana(VentanaActual); break;

case IDV-PRUEBA: VentanaActual=new C-VentanaDePrueba(C1iente); Cliente->AsociaVentana(VentanaActual) ; <- (1) break ;

/ / -- Dialogos -- / /

case IDD-CARGAR:

[ . - . I

(1) esta línea sólo es necesaria si necesitamos que el cliente pueda comunicarse con su ventana una vez que esta invoca a alguna de sus operaciones públicas(de1 cliente). En este caso también es necesario incluir el archivo de la declaración de la clase VentanaDePrueba dentro del archivo de definición de la clase cliente.

62

4.- Finalmente, podemos utilizar nuestra ventana.

Desde el cliente sólo basta con incluir la siguiente línea (notese que el cliente tiene que tener una referencia al manejador).

Mane]ador->SeleccionaVentana(IDV-PRUEBA,this); / / Pide su ventana, esta toma el control.

Y listo!

Estos pasos se tienen que repetir siempre que se quieran agregar nuevas ventanas, de ahí que podría ser útil una herramienta que automatizara este proceso.

6.3 Cambios de contexto para múltiples áreas gráficas.

El punto que trataremos a continuación tiene que ver con el empleo de la librería gráfica OpenGL dentro del ambiente de ventanas que crea xforms.

La librería xforms permite emplear áreas especiales dentro de las formas o ventanas llamadas 'canvas'. Estas áreas relegan al programador la tarea de manejar la interacción. Existe un tipo de área en particular llamado glcanvas que permite el despliegue de gráficas a partir de la librería OpenGL.

OpenGL es una libería portable que hace uso de los recursos de hardware para crear gráficas de alto rendimiento. Consiste de un conjunto de funciones que permiten desplegar 'primitivas' gráficas, tales como puntos, líneas, poligonos y también permite iluminar y mover una cámara. Se trata, al igual que xforms, de una librería de funciones en lenguaje C.

El problema con el que nos enfrentamos al utilizar estas dos librerías juntas fue en el momento en que quisimos tener varias áreas gráficas dentro de una misma forma. El problema ocurre pues cada área gráfica tiene un 'rendering context' y es necesario activar cada contexto antes de dibujar algo en el. Para mayor información sobre los contextos en openGL, véase "OpenGL graphics with the X window system. Version 1.3 por Paula Womack y John Leech".

La solución fue tener una operación llamada Activa() dentro del área gráfica.

boo1 C-AreaGrafica::Activa(Display *dpy,GLXDrawable drawlvoid *ctx) { return glXMakeCurrent (dpy, draw, (GLXContext) ctx) ;

Del lado de las ventanas que tienen el área gráfica, se tiene que llamar a esta función desde RecibeEventoO, la función que aparte de DespachaCallback() se declara 'static' y que es el callback asignado a los eventos que ocurren en el área gráfica. (Ejemplo tomado de C-VentanaPrincipal)

63

int C-VentanaPrincipal::RecibeEvento(FL - OBJECT *ob, Window win,int w,int h, XEvent *xev, void *Ud) { C - Ventanaprincipal *ApTemp; / / Apuntador temporal ApTemp=(C-Ventanaprincipal *)ob->form->u-vdata; / / Rescata el this

ApTemp->Pantalla.Activa(fl display,win,ApTemp->forma->canvas->u vdata); ApTemp->Pantalla. CambiaTamZno (w, h) ;

ApTemp->RefrescaAreaGrafica();

/ / Envia-actualizaciones a la pantalla

return O;

Es necesario llamar a la operación Activa desde aqui, ya que sólo RecibeEventoO recibe los parámetros necesarios.

Si se tienen múltiples áreas gráficas, se resuelve teniendo dentro de la clase un atributo que indique cual de las tres áreas debe ser activada.

6.4 Desplegando imágenes con OpenGL.

La aplicación que hemos desarrollado consiste principalmente del despliegue de imágenes en areas de dos dimensiones. Aunque la librería OpenGL permite dibujar en 3D, hemos trabajado con formato de enteros (OpenGL permite trabajar múltiples formatos), con la esquina superior izquierda como origen (0,O).

Sin embargo, al querer desplegar imágenes, OpenGL resulto no ser tán rapido como se esperaba. Aunque dispone de funciones que permiten desplegar un arreglo de puntos (en particular glDrawPixels()), estas funciones resultaron ser identicas en velocidad que el despliegue de la imagen 'punto por punto'.

Finalmente fue más útil el despliegue punto a punto pues permitio el uso de funciones de escalado de imágenes.

6.5 Implementación de itoa en una clase de funciones externas.

Al querer mostrar un número en xforms, muchas veces es necesario desplegarlo como cadena. Sin embargo, la función 'itoa()' que cambia un entero a caracteres ASCII, no es una función 'estándar'. Para implementarla se creo una clase 'exclusiva' para ella, se llama CExterns.

Está declarada como sigue:

#ifndef Externs - C #define Externs-C

class C-Externs I public :

64

void itoa(int,char * ) ; / / Convierte entero a ascii 1;

#endif

Así cuando se necesita llamar a la función, se hace uso de un objeto instanciado a partir de esta clase.

Esta solución es poco elegante, sin embargo, permite respetar el modelo.

6.6 Uso de 'make' para compilar la aplicación.

Para compilar la aplicación hemos hecho uso de la herramienta 'make'. A continuación se muestra el archivo 'makefile' que se empleo para la primera versión.

# Makefile de la aplicacion de reconstruccion 3D por # Humberto Cervantes para el proyecto terminal 1998.

# OBJECTS es una macro que indica todos los objetos de los cuales depende # la aplicacion para compilarse de manera exitosa.

OBJECTS=Main.o Ap1icacion.o Manejador.0 VentanaPrincipa1.o VentanaSe1Algoritmo.o Stack.0 Dia1ogoArchivo.o 1magen.o Co1or.o AreaGrafica.0 VentanaConfStack.~ VentanaConfStackDisp.0 StackConfig.0 Externs C.0 Co1eccion.o Procesador.0 ProcSegUmbra1.0 VentanaConfSegUmbra1.0 Histo5rama.o VentanaReconstruccion.~ ProcReconstruccion.~ Punto.0 ListaLigada.0 Vo1umen.o VentanaAcercaDe.0

# A continuacion aparecen macros que indican las librerias con las cuales se # debe ligar la aplicacion

LIBFORMS=-L ../FORMS -1forms LIBOGL=-lX11 -lXm -lGL

# INCXFORMS es una macro que indica que directorios buscar para incluir los # archivos de cabecera de xforms.

INCXFORMS=-I ../FORMS

# VENTANAS es una macro que indica todos los archivos de los cuales depende el # manejador.

VENTANAS=Ventana.h VentanaPrincipa1.h VentanaSelA1goritmo.h Dia1ogoArchivo.h VentanaC0nfStack.h VentanaConfSegUmbra1.h VentanaReconstruccion.h VentanaAcercaDe.h

#WARNINGS si esta definido como =-w no se mostraran warnings a la hora de compilar . . .

WARNINGS=-fullwarn -03

65

# - APLICACION DE MANERA GLOBAL -

Main: ${OBJECTS} CC -o $ @ ${OBJECTS} SILIBFORMS} StLIBOGL} -1m

# - INDEPENDIENTES DE SISTEMA DE VENTANA Y GRAFICOS -

Main.~: Main.cpp Ap1icacion.h CC ${WARNINGS} -c Main.cpp

Ap1icacion.o: Aplicacion.cpp Ap1icacion.h 0bjetoInt.h Manejador.0 Stack.0 Procesador.0 ListaLigada.0 CC ${WARNINGS} -c Aplicacion.cpp

Co1eccion.o: Coleccion.cpp Co1eccion.h 0bjetoInt.h 0bjetoModificable.h AreaGrafica.0

CC ${WARNINGS} -c Coleccion.cpp

Stack.0: Stack.cpp Stack.h Co1eccion.o 1magen.o StackConfig.0 ListaLigada.0 CC ${WARNINGS} -c Stack.cpp

StackConfig.0: StackC0nfig.h StackConfig.cpp CC ${WARNINGS} -c StackConfig.cpp

1magen.o: 1magen.cpp 1magen.h Genera1.h ListaLigada.0 DatoDeCo1eccion.h CC ${WARNINGS} -c 1magen.cpp

Co1or.o: Color.cpp Co1or.h Genera1.h ListaLigada.0 CC ${WARNINGS} -c Color.cpp

Procesador.~: Procesador.cpp Procesad0r.h Proces0.h CC ${WARNINGS} -c Procesador.cpp

ProcSegUmbra1.0: ProcSegUmbral.cpp ProcSegUmbra1.h Proces0.h CC ${WARNINGS} -c ProcSegUmbral.cpp

ProcReconstruccion.~: ProcReconstruccion.cpp ProcReconstruccion.h Proces0.h Volumen. o CC ${WARNINGS} -c ProcReconstruccion.cpp

Histograma.0 : Histograma.cpp Hist0grama.h 1magen.o CC ${WARNINGS} -c Histograma.cpp

Punto.0 : Punto.cpp Punt0.h Genera1.h ListaLigada.0 DatoDeCo1eccion.h CC ${WARNINGS} -c Punto.cpp

ListaLigada.0 : ListaLigada.cpp ListaLi9ada.h Genera1.h

66

CC ${WARNINGS} -c ListaLigada.cpp

Vo1umen.o : Volumen.cpp Vo1umen.h Genera1.h Punto.0 Co1or.o CC ${WARNINGS} -c Volumen.cpp

# -EXTERNOS DE C-

Externs-C.o:Externs-C.h Externs-C.cpp CC ${WARNINGS} -c Externs-C.cpp

# - CONTIENEN COMANDOS DE XFORMS-

Manejador.0: Manejador.cpp Mane1ador.h 0bjetoInt.h ${VENTANAS} CC ${INCXFORMS} ${WARNINGS} -c Manejador.cpp

VentanaPrincipa1.0: VentanaPrincipal.cpp VentanaPrincipa1.h Genera1.h Ventana.h Ap1icacion.o AreaGrafica.0 Color.0 CC ${INCXFORMS} ${WARNINGS} -c VentanaPrincipal.cpp

VentanaSe1Algoritmo.o: VentanaSelAlgoritmo.cpp VentanaSelA1goritmo.h Genera1.h Ventana.h Ap1icacion.o CC ${INCXFORMS} ${WARNINGS} -C VentanaSelAlgoritmo.cpp

VentanaConfStack.0: VentanaConfStack.cpp VentanaC0nfStack.h Genera1.h Ventana.h Stack.h CC ${INCXFORMS} ${WARNINGS} -c VentanaConfStack.cpp

VentanaConfStackDisp.o: VentanaConfStackDisp.cpp VentanaC0nfStackDisp.h Genera1.h Ventana.h Stack.0 StackConfig.~ CC ${INCXFORMS} ${WARNINGS} -c VentanaConfStackDisp.cpp

VentanaConfSegUmbra1.0: VentanaConfSegUmbral.cpp VentanaConfSegUmbra1.h Genera1.h Ventana.h Stack.0 StackConfig.0 AreaGrafica.0 Histograma.0 CC ${INCXFORMS} ${WARNINGS} -c VentanaConfSegUmbral.cpp

Dia1ogoArchivo.o: DialogoArchivo.cpp Dia1ogoArchivo.h Genera1.h 0bjetoInt.h Ventana.h Stack.0 CC ${INCXFORMS} ${WARNINGS} -c DialogoArchivo.cpp

VentanaReconstruccion.~: VentanaReconstruccion.cpp VentanaReconstruccion.h Genera1.h Ventana.h AreaGrafica.0 Stack.0 ProcReconstruccion.~ CC ${INCXFORMS} ${WARNINGS} -c VentanaReconctruccion.cpp

VentanaAcercaDe.0: VentanaAcercaDe.cpp VentanaAcercaDe.h Genera1.h Stack.h CC ${INCXFORMS} ${WARNINGS} -c VentanaAcercaDe.cpp

# -CONTIENEN COMANDOS DE OPENGL-

AreaGrafica.0: AreaGrafica.cpp AreaGrafica.h Genera1.h Co1or.o 1magen.o CC ${WARNINGS} -c AreaGrafica.cpp

67

Notese que estan separados los archivos que se pueden compilar independientemente de las librerias, los que conciernen a xforms y finalmente aquellos que conciernen a openGL.

7.- Conclusiones.

A raíz del desarrollo de esta aplicación, se obtuvo principalmente experiencia en el análisis y desarrollo de aplicaciones orientadas a objetos. Se pudo comprobar que la metodología propuesta por Booch es efectiva pues al final se llego a una aplicación que cumplió los requerimientos propuestos.

Algunas cuestiones que surgen a partir de esta experiencia son:

La necesidad de hacer uso de las herramientas que generan código:

El hacer uso de la herramienta Rational Rose de manera incompleta impidio acelerar el proceso de desarrollo. Es importante pues tratar de aprovechar este tipo de herramientas que ahorran mucho trabajo,

Los problemas de encapsular una libería de interfase de usuario dentro del modelo:

La libería xforms es muy útil. Sin embargo los problemas que surgieron tuvieron que ser resueltos de maneras poco 'elengantes', pues tener que escribir las funciones que despachan mensajes es algo de muy 'bajo' nivel que quita tiempo. Los 'trucos' para rescatar el apuntador 'this' y el contexto de OpenGL hacen que el código no sea completamente claro. La solución a esto sería emplear una librería orientada a objetos que no tenga problemas con el despliegue de gráficos de OpengGL.

La flexibilidad del lenauaie IC++':

El desarrollo de esta aplicación permitio comprobar que el lenguaje IC++' es un lenguaje muy flexible y poderoso. Puede resultar complicado, pero se obtienen resultados buenos con su uso.

Aun quedan algunos aspectos por probar de esta aplicación. En particular, el poder comprobar si en verdad se puede adaptar a cualquier tipo de procesamiento que se necesite, y si se puede portar sin muchas dificultades. Estos dos puntos están fuera del alcance de este proyecto, sin embargo esperamos algun día verlos llevados a cabo.

68

Bibliografía. Scott W. Ambler: The Object-Oriented Modeling Process: An Architecture Driven Approach. 30/07/97

www.ambysoft.com/ooModelingProcess.pdf

The Unified Modeling Languakge V1.1 & Beyond: The techniques of O 0

www.ambysoft.com/umlAndBeyond.pdf Modeling. 20/09/97

Bergner,Rausch,Sihling del Technishe Universitat Munchen. "Using UML for Modeling a Distributed Java Application".

TUM-I9735 Julio 1997

Booch, Grady : Análisis y diseño orientado a objetos con aplicaciones. Addison Wesley (c) 1996 Object Solutions. Managing the Object Oriented Project Addison Wesley (c) 1996

Gamma, Helm, Johnson, Vlissides : "Design Patterns - Elements of reusable Object Oriented Software" (c) 1995, Addison Wesley.

Neider, Davis, Woo : OpenGL programming guide - The official guide to learning OpenGL rel. 1 A. Wesley (c) 1993

Wiegers. : Software Development Magazine p50-55, Marzo de 1997 www.sdmagazine.com

69

Versión 1.0 Dara UNlX

Guía del Usuario

POR Humbeno Cervantes Maceda

3DOo Versión 1.0 : Guía del usuario.

Indice:

1 .- Instalando y ejecutando 3Doo ......................................................... 1 2.- Cargar un archivo ............................................................................ 2

4.- Aplicando un proceso. ..................................................................... 5 4.1 - Segmentación por Umbral ................................................. 5 4.2 - Reconstruccion.. ................................................................ .7

5.- Extras ............................................................................................... 8

3 .- Modificación del despliegue. .......................................................... 4

.,

1.- Instalando y ejecutando 3Doo.

El programa 3D00 esta diseñado para correr en computadoras con ambiente gráfico. La versión para UNIX emplea una librería llamada xforms que facilita la creación de interfaces gráficas, y que emplea el protocolo Xwindow.

Para poder correr el programa 3Doo se necesita tener las librerías xforms y OpenGL disponibles. Si estas ya se encuentran instaladas, es necesario configurar una variable de ambiente con la ruta de las librerías xforms, el nombre de la variable es:

LD - LIBRARY - PATH

Para ksh el comando para configurar esto es:

$ LDLIBRARYPATH=/directorio $ export $LD-LIBRARY-PATH

Para csh el comando para configurar esto es:

YO setenv LD-LIBRARY-PATH /directorio

La instalación del programa 3Doo es sencilla. Basta con tener el programa ejecutable en el directorio deseado y correrlo.

$ Main

Aparece entonces una ventana vacía que presenta un menú en la parte superior:

1

Las opciones del menú son:

0 Archivo Proceso Despliegue Ayuda

En este momento esta listo el programa para ser utilizado. A continuación se explica cómo cargar un archivo.

2.- Carpar un archivo.

Presionar sobre el menú archivo y enseguida sobre la opción Cargar, aparece entonces una ventana de selección de archivos:

2

Nota: La versión 1 .O sólo soporta archivos tipo ‘crudo’, es decir que contienen únicamente la información de las imágenes. Los de 1 y 2 bytes deben contener información de tonos de gris. Los de 3 bytes deben contener triadas (rojo, verde, azul) y permiten el despliegue de imágenes de color. La extensión no es importante.

Una vez que se ha seleccionado el stack de imágenes a cargar, aparece la ventana de configuración del archivo:

Se deben introducir de forma obligatoria el número de imágenes, el tamaño de la imagen, y los bytes por pixel. Los otros campos son optativos en esta versión y no afectan el uso del programa.

Cuando se han introducido los datos correctamente, el programa comienza a cargar y si esta operación se realiza de manera exitosa, debe aparecer en la parte inferior derecho un pequeño cuadro verde que dice “ok”, y en el área de comentarios debe aparecer la línea “listo”.

A continuación se muestra una conjunto de imágenes que han sido cargados correctamente:

3

3.- Modificación del despliepue.

Una vez que hemos cargado un conjunto de imágenes, estas se muestran al 100 % de su tamaño original, y posiblemente no aparezcan todas las imágenes que componen al stack, podemos modificar ciertos parámetros de despliegue que nos permitirán apreciar con mayor claridad alguna imagen en particular o el conjunto de las imágenes.

Si queremos ver una imagen en particular, nos podemos mover mediante las flechas de la parte inferior izquierda hasta esa imagen y entonces presionar sobre el boton de ‘zoom’. La imagen en particular aparecerá sola en la pantalla y la aplicación tratará de ajustarla al máximo tamaño posible sin distorsionarla. En la parte inferior derecha, el status pasara a ‘zoom’ y el color de fondo del aviso será azul (cyan).

A continuación se muestra una imagen en modo ‘zoom’.

En este modo se puede recorrer todo el stack mediante las flechas de posición del cursor.

Sin embargo, puede resultar lento recorrer el stack de esta forma, para ver todo el stack en su conjunto, debemos presionar en el menú “despliegue” y enseguida seleccionar la opción “configurar”. Nos debe aparecer una ventana como la siguiente:

4

En esta ventana de configuración del stack, se puede seleccionar la imagen inicial a desplegar. Por omisión este valor es igual a 1. La escala de despliegue también se puede configurar y al mover el control concerniente a este valor, la línea que dice “con esta escala se desplegaran ... imágenes” se actualizará automáticamente para mostrar la cantidad de imágenes que aparecerán en el despliegue.

Es posible configurar también la intensidad con que se despliegan las imágenes, el valor por omisión de este parámetro es igual a 100%. En la versión 1 .O, las imágenes siempre aparecen con marco, y su numeración aparece en el lado inferior izquierdo de la ventana principal, por lo cual los botones de “poner marco” y “numerar imágenes” no cumplen ninguna función.

Cuando se presiona listo, el control regresa a la ventana principal, y esta debe reflejar los cambios que se hayan seleccionado.

4.- Aplicando un proceso.

Para aplicar un proceso, este debe ser seleccionado primero. Para ello presionar en el menú “proceso” la opción “seleccionar proceso”, debe aparecer una ventana como la siguiente:

4.1 Segmentación por umbral.

En la versión 1.0 sólo aparecen dos tipos de procesos: La segmentación por umbral y la reconstrucción. Para realizar el segundo, se debe primero hacer una segmentación. Si se selecciona la opción de segmentación por Umbral, aparecerá la ventana de configuración del algoritmo, similar a la siguiente:

5

Podemos ver que esta ventana muestra dos imágenes, un histograma y varios controles. La imagen a la izquierda es con la que se está trabajando. Se puede cambiar de imagen mediante el control de la parte inferior que dice “imagen actual”. Al cambiar de imagen, el histograma se actualiza de manera automática.

La imagen segmentada muestra el resultado de la segmentación por umbral de la imagen actual de acuerdo a los limites establecidos dentro del histograma. Para actualizar esta imagen, se debe presionar sobre “segmentar actual”.

Para mover los limites dentro del histograma, basta mover los dos controles de selección. El superior corresponde al limite izquierdo, y el inferior al derecho. Los limites aparecen dentro del histograma como líneas amarillas que de un lado dejan ver el histograma y del otro reducen todos los valores a cero.

Si se desea ver algun detalle del histograma, se pueden hacer agrandamientos de los valores, esto al presionar los botones [+I y [-I del zoom para X o para Y.

Una vez que se esta conforme con los limites seleccionados, basta presionar aplicar, y en la esquina inferior izquierda aparecerá un contador que muestra la progresión del algoritmo. Cuando este llegue a 100% estará terminado.

La ventana de configuración desaparece y el control regresa a la ventana principal, sin embargo ahora se muestra el resultado de la segmentación, es posible efectuar cambios en el despliegue de este stack de la misma forma que para el stack original.

6

Si se desea volver al stack original, en el menú despliegue habrá aparecido que dice stack #1. El stack #O corresponde al original, el #1 al segmentado. A continuación se muestra un stack segmentado:

una línea extra

4.2 Reconstrucción.

Si ahora deseamos realizar una reconstrucción, seleccionamos de nuevo “proceso”, pero esta vez seleccionamos “Contorno/Reconstrucción”.

Cuando seleccionamos este proceso, aparece la ventana apropiada. En este proceso no se necesitan configurar parámetros, al aparecer la ventana, comienza el calculo de contornos y después el de normales. Cuando termina este proceso aparece la reconstrucción. A continuación se muestra la ventana correspondiente a este proceso:

7

En esta ventana aparecen tres áreas gráficas. Las dos de la izquierda muestran los cortes originales y sus contornos correspondientes. Es posible recorrer el conjunto de imágenes con el control. En el recuadro junto a la palabra ‘Contorno’ aparece el número de imagen que se está desplegando. En el área gráfica principal, aparece el resultado de la reconstrucción. Al centro se muestra el origen con ejes de tres colores, el eje X se muestra de color rojo, Y en verde y Z en azul. Del lado derecho se puede controlar la rotación, la translación y el zoom de la imagen. Los campos que muestran los valores pueden ser modificados directamente para evitar tener que presionar muchas veces sobre los controles + y -.

Cuando uno decide salir de la reconstrucción aparece en la ventana principal el conjunto de contornos, que puede ser manipulado de igual forma que el segmentado.

5. Extras.

Para salir de la aplicación basta presionar el botón marcado con una ‘X’

La opción “ayuda” del menú principal nos muestra una opción “acerca de” que al ser seleccionada hace aparecer la ventana siguiente:

La versión 1 .O no tiene una ayuda formal. Sin embargo se puede consultar todo lo referente al programa en la siguiente dirección de internet.

htip://itzamna.uam.m~umberto~OCUMENTACION/Indice. html

Cualquier comentario es bienvenido a la dirección de correo siguiente:

hcc(íu,h~9000al .uam.mx

8