Archivos Mensuales: junio 2013

Insertando vértices a una línea seleccionada gráficamente en sus intersecciones con otras líneas

Hoy vamos a añadir una nueva orden a nuestro proyecto OrdenesDigiNG (cuyo código fuente puedes descargar de nuestro repositorio en GitHub

Esta orden va a solicitar al usuario que seleccione una línea e insertará en esa línea tantos vértices como intersecciones tenga esa línea con el resto de entidades visibles.

Para ello vamos a seguir los siguientes pasos:

  1. Cargamos el proyecto OrdenesDigiNG con Visual Studio 2010
  2. Añadimos una clase que denominaremos TramificaInsertandoEntidadSeleccionada
  3. Hacemos que la clase sea pública para que Digi3D.NET la pueda instanciar.
  4. Añadimos el atributo CommandAttribute asignando como nombre público de la orden tramifica_insertando_entidad_sel
  5. Hacemos que la clase herede del tipo Command (recuerda añadir la cláusula using Digi21.DigiNG.Plugin.Command)
  6. Añadimos un constructor público en el que vamos a añadir manejadores de eventos para los eventos Initialize, SetFocus, DataUp y EntitySelected. Los eventos Initialize y SetFocus hacemos que sean manejados por un único método que vamos a denominar InvitaAlUsuarioASeleccionarLínea que tendrá la firma de un manejador de eventos clásico.

    El constructor y el manejador de eventos tendrán el siguiente aspecto:

    public TramificaInsertandoEntidadSeleccionada()
    {
        this.Initialize += InvitaAlUsuarioASeleccionarLínea;
        this.SetFocus += InvitaAlUsuarioASeleccionarLínea;
        this.DataUp += new EventHandler<Digi21.Math.Point3DEventArgs>(TramificaInsertandoEntidadSeleccionada_DataUp);
        this.EntitySelected += new EventHandler<EntitySelectedEventArgs>(TramificaInsertandoEntidadSeleccionada_EntitySelected);
    }
    
    void TramificaInsertandoEntidadSeleccionada_DataUp(object sender, Digi21.Math.Point3DEventArgs e)
    {
        throw new NotImplementedException();
    }
    
  7. Ahora vamos iniciamos el proceso de selección de líneas en el manejador de eventos del evento DataUp.

    Para ello llamaremos a la sobrecarga del método DigiNG.SelectEntity que dispone de un predicado en el segundo parámetro e indicaremos mediante una expresión Lambda que únicamente vamos a permitir seleccionar entidades de tipo ReadOnlyLine.

    El método queda así pues:

    void TramificaInsertandoEntidadSeleccionada_DataUp(object sender, Point3DEventArgs e)
    {
        DigiNG.SelectEntity(e.Coordinates, entidad => entidad is ReadOnlyLine);
    }
    

Ahora vamos a realizar nuestra tarea con la línea seleccionada.

  1. Vamos a crear dos conjuntos: uno con la línea que queremos tramificar y otro con el resto de líneas del archivo de dibujo teniendo en cuenta que la orden únicamente debe trabajar con las entidades que sean visibles en este momento. Para esto último podemos utilizar el método de extensión Visibles del proyecto UtilidadesDigi que puedes descargar de nuestro repositorio de GitHub.

    var líneaATramificar = e.Entity as ReadOnlyLine;
    
    // Seleccionamos las líneas visibles del archivo de dibujo (excluyendo la línea que vamos a tramificar)
    var líneasContraLasCualesTramificar = from entidad in DigiNG.DrawingFile.OfType<ReadOnlyLine>().Visibles()
                                            where entidad != e.Entity
                                            select entidad;
    
  2. Después vamos a obtener una lista de las intersecciones de la línea seleccionada con el resto de líneas utilizando el método de extensión DetectIntersections:

    // Obtenemos las intersecciones de la línea seleccionada con el reto de líneas visibles
    var intersecciones = líneaATramificar.DetectIntersections(líneasContraLasCualesTramificar);
    

    Este método devuelve una agrupación de coordenadas y de entidades que llegan a estas coordenadas. Te recuerdo que cada coordenada es en sí mismo una secuencia cuyo contenido son valores de tipo SegmentPointer que indican por un lado la lína que llega a esa coordenadas y entre que vértices de esa línea se ha localizado la intersección.

  3. Ahora vamos a construir nuestra línea nueva.
    La línea nueva tendrá al menos tantos vértices como la línea original. Cada vértice es el comienzo de un segmento. El vértice 0 es el primer vértice del segmento 0. El vértice 1 es el primer segmento del segmento 1 y así sucesivamente.
    Lo que vamos a hacer es copiar el primer vértice del primer segmento de la línea original a la nueva. Luego buscaremos todas las intersecciones localizadas en ese determinado segmento de y las añadiremos a la línea destino, pero atención, tendremos que ordenar las intersecciones, porque nadie nos garantiza que se nos estén devolviendo ordenadas.

    for (int vértice = 0; vértice < líneaATramificar.Points.Count - 1; vértice++)
    {
        // Añadimos el vértice de la línea original
        líneaNueva.Points.Add(líneaATramificar.Points[vértice]);
    
        // Ahora localizamos únicamente las intersecciones localizadas para el segmento actual en la línea a tramificar
        var vérticesAAñadirEnEsteSegmento = intersecciones.SoloDeSegmento(líneaATramificar, vértice);
    
        // Tenemos una lista de vértices, pero pueden venir desordenados. Vamos a ordenarlos calculando su distancia a la coordenada del primer vértice de este segmento
        Point2D vérticeComienzoSegmento = (Point2D)líneaATramificar.Points[vértice];
    
        var vérticesOrdenados = from v in vérticesAAñadirEnEsteSegmento
                                let distancia = (v.Key - vérticeComienzoSegmento).Module
                                orderby distancia
                                select v.Key;
    
        // Ahora insertamos estos vértices en la línea nueva. Los vértices son 2D (los métodos de extensión proporcionados por el tipo IntersectionDetector trabajan con Point2D
        // así que tendremos que ínterpolar la coordenada Z
        foreach (var v in vérticesOrdenados)
        {
            var segmento = new Segment(líneaATramificar.Points[vértice], líneaATramificar.Points[vértice + 1]);
            double z = segmento.InterpolatedZ(new Point3D(v));
    
            líneaNueva.Points.Add(new Point3D(v.X, v.Y, z));
        }
    }
    
    // Por último añadimos el último vértice
    líneaNueva.Points.Add(líneaATramificar.Points.Last());
    
  4. Y ¡ya hemos terminado casi!
    Tan solo nos queda añadir la entidad nueva al archivo de dibujo, eliminar la anterior y destruir la orden:

    DigiNG.DrawingFile.Add(líneaNueva);
    DigiNG.DrawingFile.Delete(e.Entity);
    Dispose();