A continuación se muestra el Diagrama de Clases para la solución encontrada:
Se observan 11 clases publicas más 3 clases internas,
todas ellas asociadas al panel de dibujo. La clase Editor extiende de Frame
y a ella se agrega un objeto de tipo MainPanel,
que contendrá los paneles que se aprecian en la GUI. MainPanel fue pensada para poder organizar sus componentes, los
demás paneles, mediante métodos asociados a Layouts
de Java. Los demás paneles se han creado desde herencia de JPanel, lo que permite un manejo mas modular de la solución. La
clase Editor contiene además el código asociado a la administración de
archivos y la implementación de la barra de menú y la atención de sus eventos,
para lo cual ha implementado la interfaz ActionListener.
En MainPanel se agregan los paneles de Acción, Figuras y Dibujo (Drawing). Los dos primeros tienen instancias de la clase Botones, que fue diseñada para poder personalizar los botones con iconos y formato. La clase ButtonHandler se encarga de manejar los eventos de botón, que pueden provenir de las distintas clases asociadas. El DrawingPanel no contiene botones, pero está “escuchando” el estado de ellos para saber que método invocar cuando se desee ejecutar alguna Acción o dibujar alguna Figura.
Finalmente, dentro de la clase DrawingPanel se han implementado los métodos para cumplir con la interfaz de MouseAdapter, asociada a movimientos y eventos relacionados con el mouse. La clase interna ComponentHandler aparece para identificar un resize de la ventana y aislar la invocación del método paintComponent en relación con los llamados para dibujar o realizar AffineTransforms.
La AffineTransform de rotación es utilizada mediante el método rotate() directamente sobre el componente gráfico. Sin embargo, al utilizarla se debe tener en mente los conceptos de user space y device space, esto es, las coordenadas de usuario y de dispositivo. Java se basa en las coordenadas de dispositivo, para ubicar sus ventanas y sus paneles y posicionarse adecuadamente en el despliegue que se este usando. El espacio de usuario es donde se hacen las transformaciones, y en este caso está asociado al panel de Dibujo, que sobremonta el método paintComponent para efectuar el repintado de las figuras.
Las clases MyLine, MyCirc y MyRect heredan respectivamente de Line2D.Double, Ellipse2D.Double y Rectangle2D.Double y además implementan las interfaz Serializable. Con esto se evita la declaración de las instancias como transient y además se tiene cómodo acceso a los atributos de la clase padre, sus constructores y sus métodos. Estas clases contienen métodos que implementan las traslaciones y escalamientos, para así modificar realmente los datos y no solo la visión de estos y los métodos ya mencionados para permitir la serialización de las figuras.
El formato de almacenamiento de los archivos .eda fue pensado para permitir una lectura eficiente y simple de ellos. De este modo, se almacenan objetos en el siguiente orden:
· Primero se escribe un entero con writeInt que contiene el tamaño del arreglo de figuras.
· En segundo lugar se recorre el arreglo de figuras, invocando el método writeObject para cada una. Según la clase de la figura, el ligado dinámico llama al método apropiado definido en dicha clase para guardar los atributos de la figura. En el caso de las MyLine’s, se almacena el par de puntos; los MyRect’s guardan el ancho, alto y punto izquierdo superior, y lo mismo corre para los MyCirc’s. Los almacenamientos se hacen con writeDouble en todos los casos.
· Finalmente, se almacena la transformación de rotación. Para ello se obtiene el valor actual del ángulo y la coordenada del punto de anclaje, siendo escritos al stream mediante el método writeDouble. Se prefirió no almacenar directamente el objeto AffineTransform, ya que se experimentaron problemas con la inclusión de la transformación una vez que se cargaba el archivo. Resultó mejor retornar los datos de la transformación desde el archivo y configurar una sola AffineTransform de rotación, para implementarla de vuelta directamente por un método en el panel de dibujo.
Para leer el archivo .eda basta con seguir un procedimiento análogo al de almacenado. La ventaja de almacenar el tamaño del arreglo de figuras como un entero es que se recupera el inicio del proceso de carga de archivo y de inmediato se configura un ciclo for para leer las figuras que correspondan. Una vez hecho esto, basta solo con leer a un arreglo de Double las variables de ángulo y coordenadas de anclaje x e y e invocar al método de la instancia de DrawingPanel que se ha creado.
Formato de archivos .fig
El formato de archivo para las Xfigs fue implementado a través de escrituras en un archivo de texto, mediante una instancia de la clase PrintWriter. Se configuró un encabezado estándar, en que figuraban la orientación, tamaño de página, resolución y otros. Además, para cada uno de los tres tipos de figura, existían sub-encabezados que se mantenían iguales, ya que identificaban el estilo de línea, el código xfig para la figura (1 1 para circulo, 2 1 para polyline y 2 2 para rectangulos) entre otros. Finalmente, para cada figura en particular se determinan los parámetros xfig para almacenar. Es así como para el circulo se debe entregar el punto del centro, el radio y los puntos limites para un bounding. Para el rectángulo se debe separar en cuatro líneas, entregando para cada una el punto de inicio y de fin. En el caso de las polylines se optó por la forma más simple, esto es, escribir separadamente las líneas una por una, en vez de agruparlas como polylines. Este procedimiento no afecta en nada la visualización del archivo .fig pero simplifica su estructuración.
Javadoc
La documentación en Javadoc se encuentra aquí.
Chequeo de Estilo
Para verificarlo use make style en el directorio donde se ubica el Makefile. Esta llamada considera que el archivo checkstyle-all-3.4 se encuentra en /usr/local/checkstyle/checkstyle-all-3.4.jar y que el archivo de estilo sjsu.xml, incluido con esta Tarea, se ubique en el directorio en que se encuentra el código java. El chequeo de estilo pasa correctamente, a excepción de un “error” en la línea 26 columna 10 de la clase ImageFilter. El error pareciera indicar que hay lógica condicional superflua, por lo que sugiere remover dicha sentencia. Sin embargo, se cree que el chequeo de estilo incurre en un error, ya que no es posible remover dicha sentencia condicional sin afectar el correcto funcionamiento del filtro de extensiones. Se considera no relevante dicho error y, de este modo, el código está escrito cumpliendo correctamente las reglas de estilo.