Archivos Mensuales: febrero 2013

Convirtiendo los métodos de extensión sobre secuencias de entidades en métodos genéricos

Vamos a realizar un cambio a la biblioteca de clases UtilidadesDigi que hemos ido construyendo en nuestro curso de programación de Digi3D:

Si te fijas en el código de los métodos de extensión UtilidadesSecuenciaEntity.QueTenganElCódigo, UtilidadesSecuenciaEntity.QueTenganElCódigoConComodín, UtilidadesSecuenciaEntity.QueTenganAlgúnCódigo y UtilidadesSecuenciaEntity.QueTenganAlgúnCódigoConComodín:

    public static IEnumerable<Entity> QueTenganElCódigo(this IEnumerable<Entity> secuenciaOriginal, string código)
    {
        return from entidad in secuenciaOriginal
               where entidad.TieneElCódigo(código)
               select entidad;
    }
    public static IEnumerable<Entity> QueTenganElCódigoConComodín(this IEnumerable<Entity> secuenciaOriginal, string código)
    {
        return from entidad in secuenciaOriginal
               where entidad.TieneElCódigoConComodín(código)
               select entidad;
    }
    public static IEnumerable<Entity> QueTenganAlgúnCódigo(this IEnumerable<Entity> secuenciaOriginal, IEnumerable<string> códigos)
    {
        return from entidad in secuenciaOriginal
               where entidad.TieneAlgúnCódigo(códigos)
               select entidad;
    }
    public static IEnumerable<Entity> QueTenganAlgúnCódigoConComodín(this IEnumerable<Entity> secuenciaOriginal, IEnumerable<string> códigos)
    {
        return from entidad in secuenciaOriginal
               where entidad.TieneAlgúnCódigoConComodín(códigos)
               select entidad;
    }

Siempre devuelven una secuencia de IEntity, aunque esa secuencia esté particularizada para un determinado tipo de entidad, y eso deshabilita todos los filtros que se hubieran aplicado antes, como en el siguiente ejemplo:

            var líneasDeMarcoHoja = DigiNG.DrawingFile.OfType<ReadOnlyLine>().QueTenganElCódigo(códigoDelMarco);

Vamos a estudiar que pasa con la línea de código anterior:

  • DigiNG.DrawingFile devuelve una secuencia de IEnumerable<IEntity>.
  • Generamos una secuencia de ReadOnlyLine mediante el método de extensión de Linq: OfType<T>. A partir de este momento tenemos una secuencia: IEnumerable<ReadOnlyLine>.
  • Luego llamamos al método de extensión UtilidadesDigiNG.QueTenganElCódigo que extuende IEnumerable<IEntity> y que devuelve una secuencia: IEnumerable<IEntity>.

Lo que resulta en que al final lo que tenemos es un IEnumerable<IEntity>, y está claro por el contexto que lo que queremos es un IEnumerable<ReadOnlyLine>.

En este caso particular, podríamos haberlo solucionado de la siguiente forma:

            var líneasDeMarcoHoja = DigiNG.DrawingFile.QueTenganElCódigo(códigoDelMarco).OfType<ReadOnlyLine>();

Pero esa no es la solución. Tenemos que modificar nuestros métodos de extensión para que sean genéricos, de forma que si están subclasificando una secuencia de X, que lo que devuelvan sea una secuencia de X y no una secuencia de otra cosa.

Éstos métodos van a extender secuencias de tipos cuya base sea IEntity, así que cuando las convirtamos en métodos genéricos tendrán que tener como restricción que el tipo genérico herede de IEntity.

Veamos cómo quedan nuestros métodos de extensión una vez convertidos en métodos genéricos:

    public static IEnumerable<T> QueTenganElCódigo<T>(this IEnumerable<T> secuenciaOriginal, string código)
        where T : Entity
    {
        return from entidad in secuenciaOriginal
               where entidad.TieneElCódigo(código)
               select entidad;
    }

    public static IEnumerable<T> QueTenganElCódigoConComodín<T>(this IEnumerable<T> secuenciaOriginal, string código)
        where T : Entity
    {
        return from entidad in secuenciaOriginal
               where entidad.TieneElCódigoConComodín(código)
               select entidad;
    }

    public static IEnumerable<T> QueTenganAlgúnCódigo<T>(this IEnumerable<T> secuenciaOriginal, IEnumerable<string> códigos)
        where T : Entity
    {
        return from entidad in secuenciaOriginal
               where entidad.TieneAlgúnCódigo(códigos)
               select entidad;
    }

    public static IEnumerable<T> QueTenganAlgúnCódigoConComodín<T>(this IEnumerable<T> secuenciaOriginal, IEnumerable<string> códigos)
        where T : Entity
    {
        return from entidad in secuenciaOriginal
               where entidad.TieneAlgúnCódigoConComodín(códigos)
               select entidad;
    }

De esta manera estos métodos de extensión devuelven subconjuntos de secuencias sin cambiar su tipo.

Comportamiento de los importadores de archivos de dibujo sin tabla de códigos

El día 01/02/2013 hemos añadido a Digi3D.NET la posibilidad de no indicar en el cuadro de diálogo Nuevo Proyecto, en la pestaña Archivo de dibujo ninguna tabla de códigos en el campo Tabla de códigos.

En este post te voy a explicar como funciona esta característica dentro de Digi3D.NET.

Modificaciones realizadas al programa principal

Digi3D.NET necesita una tabla de códigos para poder trabajar. Es absolutamente fundamental. Todo el programa está desarrollando teniendo en cuenta de que en memoria hay una tabla de códigos. Ahí es donde aparece la representación (color, grosor, patrones,…) de cada código, parámetros de transformación, …

Se han realizado dos modificaciones a Digi3D.NET para permitir trabajar sin tabla de códigos:

  • Permitir al usuario dejar en blanco el campo correspondiente en el cuadro de diálogo de nuevo proyecto.
  • Analizar si el usuario deje ha dejado en blanco ese campo. En caso afirmativo, crear en memoria una tabla de códigos vacía, sin nombre asignado, sin ruta, sin nada (bueno, en realidad si hay algo, un código que es el código al que van a parar las entidades con códigos desconocidos). Esta tabla de códigos es la que se le pasa al importador/exportador encargado de cargar el archivo de dibujo.

Ahora bien, no solo ha habido que modificar el programa principal. También ha habido que modificar los distintos importadores/exportadores para que añadan códigos a esta tabla de códigos, y a la hora de desarrollar esa modificación surgió una duda: ¿En qué casos los importadores/exportadores van a añadir códigos?.

Decidiendo cuando se va a añadir un código nuevo en la tabla de códigos

Digi3D.NET nunca ha añadido códigos a la tabla de códigos activa si se encuentra con un código desconocido, por lo tanto no queremos que si alguien actualiza el programa, este siga comportándose así, es decir, si se encuentra un código desconocido, este no se añade a la tabla de códigos y el usuario verá ese código de color gris, lo que le hará saber que es un código desconocido.

Cuadro de diálogo de configuración

Cuadro de diálogo de configuración mostrando la opción de añadir códigos desconocidos para el importador de binarios de doble precisión.

Lo primero que se ha añadido a cada uno de los importadores/exportadores es una nueva opción en el cuadro de diálogo de Configuración que le va a permitir indicar al usuario si quiere que el importador/exportador añada automáticamente códigos a la tabla de códigos activa si no localiza en esa tabla el código con el cual traducir una determinada entidad localizada en el archivo de dibujo.

Si te fijas en la captura de pantalla, la opción por defecto es No. Es así por lo indicado anteriormente: queremos mantener la compatibilidad hacia atrás y no queremos que de repente se añadan códigos automáticamente a un usuario que indica una tabla de códigos activa. Si el usuario activa esta opción, se añadirán códigos, pero tiene que ser el usuario quien implícitamente indique que quiere que el programa se comporte de ese modo.

Sin embargo, no queremos que el usuario que se quiera aprovechar de esta funcionalidad tenga que cambiar esta opción obligatoriamente, y ahí es donde entra la posibilidad de dejar vacío el campo en el que se indica la tabla de códigos en el cuadro de diálogo de Nuevo Proyecto.

Si el usuario deja vacío ese campo, aunque tenga deshabilitada la opción de añadir códigos en el cuadro de diálogo de Configuración, los importadores van a crear códigos nuevos.

Comportamiento en función del importador/exportador

No todos los importadores/exportadores se comportan igual, por eso te pongo aquí una tabla para que veas las diferencias de comportamiento en función del formato del archivo de dibujo y en función de la configuración indicada.

Escenario 1: El usuario no ha rellenado el campo Tabla de códigos en el cuadro de diálogo Nuevo Proyecto, al cargar el archivo de dibujo o ha seleccionado una tabla de códigos pero ninguno de sus códigos se corresponden con los de las entidades del archivo de dibujo y además ha seleccionado NO AÑADIR CÓDIGOS DESCONOCIDOS en la configuración del programa para el importador en cuestión. .

Importador Comportamiento
BinD Se crearán tantos códigos como códigos se localicen en las entidades del archivo de dibujo.
Bin Se crearán tantos códigos como códigos se localicen en las entidades del archivo de dibujo.
Dwg Se crearán tantos códigos como capas se localicen en las entidades del archivo de dibujo.
Dgn Se crearán tantos códigos como códigos se localicen en las entidades del archivo de dibujo. El caso de Dgn es especial, pues la codificación varía en función del parámetro riterio para códigos seleccionado por el usuario a la hora de cargar el archivo de dibujo.
Shp Se crearán tantos códigos como tablas formen el shapefile.
Mdb (Geomedia Datawarehouse) Se crearán tantos códigos como tablas con geometrías se localicen en el .mdb.

Escenario 2: El usuario ha cargado una tabla de códigos pero ninguno de sus códigos se corresponden con los de las entidades del archivo de dibujo. El usuario ha seleccionado NO AÑADIR CÓDIGOS DESCONOCIDOS en la configuración del programa para el importador en cuestión.

Importador Comportamiento
BinD Se cargarán las entidades con código desconocido.
Bin Se cargarán las entidades con código desconocido.
Dwg Se cargarán las entidades con código desconocido.
Dgn Se cargarán las entidades con código desconocido,
Shp No se cargarán las entidades de las capas para las que no se localiza correspondencia en la tabla de códigos.
Mdb (Geomedia Datawarehouse) No se cargarán las entidades de las capas para las que no se localiza correspondencia en la tabla de códigos.

Es decir, que los importadores basados en bases de datos, como Shp y Geomedia no pueden cargar entidades con código desconocido.