Tipos de tareas

Podemos añadir tareas en el interfaz de usuario de Digi3D 2011 mediante el método Digi21.DigiNG.Tasks.Add, que espera recibir un objeto que implemente el interfaz Digi21.Digi3D.ITask.

Este interfaz tiene el siguiente aspecto:

    public interface ITask
    {
        ITask[] Childs { get; }
        string Description { get; }
        string DrawingFile { get; }
        string Module { get; }
        TaskSeverity Severity { get; }
        string Title { get; }

        void Execute();
    }

Las dos propiedades más importantes son Title que indica el título que se mostrará al usuario en la ventana de tareas, Severity que indica en que subpanel de la barra de tareas mostrar la tarea (Errores, Advertencias o Mensajes).
Si el usuario hace doble clic en una determinada tarea, se ejecutará su método Execute, que obrará en consecuencia.

No es necesario que creemos tipos que implementen el interfaz ITask, pues el framework de Digi3D.NET incluye varios tipos que somos libres de utilizar.

Tarea que muestra un cuadro de mensajes al usuario

Si queremos añadir una tarea de información al usuario que muestre un MessageBox a este cuando haga doble clic sobre la tarea, podemos utilizar el tipo Digi21.Digi3D.TaskMessageBox que implementa los siguientes constructores:

        public TaskMessageBox();
        public TaskMessageBox(
            string title,
            string description,
            TaskSeverity severity,
            string drawingFile,
            string module);
        public TaskMessageBox(
            string title,
            string description,
            TaskSeverity severity,
            string drawingFile,
            string module,
            ITask[] childs);

El primer constructor no admite comentarios, los ostros dos se diferencian únicamente porque el segundo admite que le pasemos un array de subtareas.

  • En el parámetro title debes poner el texto que quieres que aparezca como título de la tarea en el panel de tareas.
  • En el parámetro description debes poner el cuerpo del texto que quieres que aparezca en el MessageBox.
  • En el parámetro severity selecciona el panel en el que quieres que aparezca la tarea en cuestión.
  • En el parámetro drawingFile te recomiendo que pongas siempre el nombre del archivo donde se ha localizado la causa por la cual estás añadiendo una tarea. En la mayoría de los casos pondrás DigiNG.DrawingFile.Path
  • En el parámetro module te recomiendo que pongas el nombre de la orden que está añadiendo la tarea, para que el usuario sepa quién ha añadido esa tarea.

Veámoslo en acción: A continuación te presento una orden que salude al usuario. Puedes ver el vídeo Ventana de tareas para ver cómo crear el proyecto del cual te presento aquí un recorte de código.

    [Command(Name = "SaludaVentanaTareas")]
    public class SaludaVentanaTareas : Command
    {
        public SaludaVentanaTareas()
        {
            this.Initialize += new EventHandler(SaludaVentanaTareas_Initialize);
        }

        void SaludaVentanaTareas_Initialize(object sender, EventArgs e)
        {
            Digi3D.Tasks.Add(new TaskMessageBox(
                "¡Saludos!",
                "Hola usuario de Digi3D",
                TaskSeverity.Message,
                DigiNG.DrawingFile.Path,
                "SaludaVentanaTareas"));
            Dispose();
        }
    }

Si ejecutamos esta orden, aparecerá la siguiente tarea en el panel de tareas:

y si hacemos doble clic sobre la tarea, esta nos mostrará el siguiente MessageBox

Más fácil imposible.

Tarea que anima una entidad durante cierta cantidad de tiempo

El tipo Digi21.DigiNG.TaskAnimateEntity nos va a permitir animar una entidad para que el usuario sepa a qué entidad nos estamos refiriendo en la tarea. Esta animación se realiza por una cantidad de tiempo y además se realiza sin desplazar la cámara.

Este tipo implementa los siguientes constructores:

        public TaskAnimateEntity();
        public TaskAnimateEntity(
            Entity entity,
            int seconds,
            string title,
            TaskSeverity severity);
        public TaskAnimateEntity(
            Entity entity,
            int seconds,
            string title,
            TaskSeverity severity,
            string drawingFile, string
            module);
        public TaskAnimateEntity(
            Entity entity,
            int seconds,
            string title,
            TaskSeverity severity,
            string drawingFile,
            string module,
            ITask[] childs);

y si te fijas la única diferencia con el tipo anterior es que esta vez debemos pasar como parámetro la entidad que queremos animar así como el número de segundos que queremos que dure la animación.

Veámoslo en acción: A continuación te presento una orden a la que le pasamos como parámetro un número de entidad, y esta, si se superan todos los tests añade finalmente una tarea en el subpanel de mensajes de la barra de tareas que al hacer doble clic anima la entidad en cuestión.

    [Command(Name = "anima_entidad_número")]
    public class AnimaEntidadNúmero : Command
    {
        public AnimaEntidadNúmero()
        {
            this.Initialize += new EventHandler(AnimaEntidadNúmero_Initialize);
        }

        void AnimaEntidadNúmero_Initialize(object sender, EventArgs e)
        {
            try
            {
                if (this.Args.Length == 0)
                {
                    Digi3D.Music(MusicType.Error);
                    Digi3D.ShowBallon(
                        "anima_entidad_número",
                        "No has indicado el número de entidad a seleccionar",
                        2,
                        BallonIcon.Error);

                    return;
                }

                int posición;
                if( !int.TryParse(this.Args[0], out posición)) {
                    Digi3D.Music(MusicType.Error);
                    Digi3D.ShowBallon(
                        "anima_entidad_número",
                        "El parámetro pasado no es un número",
                        2,
                        BallonIcon.Error);

                    return;
                }

                if( posición < 0 || posición > DigiNG.DrawingFile.Count() ) {
                    Digi3D.Music(MusicType.Error);
                    Digi3D.ShowBallon(
                        "anima_entidad_número",
                        "No existe ninguna entidad en la posición pasada por parámetros",
                        2,
                        BallonIcon.Error);

                    return;
                }

                string título = string.Format(
                    "Entidad en la posición número {0}",
                    posición);

                Digi3D.Tasks.Add(
                    new TaskAnimateEntity(
                        DigiNG.DrawingFile.ElementAt(posición),
                        2,
                        título,
                        TaskSeverity.Message,
                        DigiNG.DrawingFile.Path,
                        "anima_entidad_número"));
            }
            finally
            {
                Dispose();
            }
        }
    }

Tarea que hace un zoom extendido en una determinada entidad

Existe un tipo muy parecido al anterior cuya finalidad es la de hacer un zoom extendido en una determinada entidad. Este tipo es el tipo Digi21.DigiNG.TaskZoomEntity y tiene los mismos constructores que el tipo anterior.
Si modificamos el código del ejemplo anterior y sustituimos TaskAnimateEntity por TaskZoomEntity podrás comprobar que el programa hace un zoom extendido en la entidad a parte de animarla.
Avanzado: El tipo
TaskZoomEntity hereda de TaskAnimateEntity.

Tarea que desplaza la cámara a un punto y además muestra un triángulo al regenerar la pantalla

Disponemos además de una tarea Digi21.DigiNG.TaskGotoPoint que desplaza la cámara a una determinada posición y además dibuja una marca (un triángulo) en las coordenadas donde se ha desplazado la cámara.
Esta tarea permite además configurar el tamaño y color (incluido el factor de opacidad) del triángulo de modo que podríamos crear tareas con distintos colores en función de un determinado criterio como verde un error poco importante y rojo uno grave.

A continuación el constructor con más parámetros de este tipo:

        public TaskGotoPoint(
            Point3D coordinates,
            Color color,
            int size,
            string title,
            TaskSeverity severity,
            string drawingFile,
            string module,
            ITask[] childs);

Como puedes observar, puedes indicar las coordenadas a las que desplazar la cámara cuando el usuario haga doble clic, el color (en el espacio de nombres System.Drawing) con el que representar en la ventana de dibujo el triángulo cuyo centro coincide en las coordenadas del primer parámetro, el tamaño en píxeles del lado del triángulo, el resto de parámetros no es necesario que te los explique.

Veámoslo en acción: A continuación te presento una orden que analiza las líneas almacenadas en el archivo de dibujo con un determinado código y que superen una determinada distancia sin decrementar la coordenada Z unas determinadas unidades.

Aquí el vídeo de esta sección de código

Supongamos que nuestro pliego de condiciones nos obliga a que las líneas de hidrografía no puedan superar una distancia de 30 metros sin decrementar la coordenada z al menos 30 centímetros. Vamos a implementar una orden que analice el archivo de dibujo en busca de esta situación y añadiremos una tarea que desplazará la cámara a la posición donde se ha descubierto el error y además animará la entidad para que el usuario sepa a que entidad nos estamos refiriendo.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Digi21.DigiNG.Plugin.Command;
using Digi21.Digi3D;
using Digi21.DigiNG;
using Digi21.DigiNG.Entities;
using UtilidadesDigi;
using Digi21.Math;
using System.Drawing;

namespace Acme
{
    [Command(Name = "detectar_lineas_con_tramos_largos_sin_modificar_z")]
    public class DetectarLíneasConTramosSinModificarZ : Command
    {
        public DetectarLíneasConTramosSinModificarZ()
        {
            this.Initialize += new EventHandler(DetectarLíneasConTramosSinModificarZ_Initialize);
        }

        void DetectarLíneasConTramosSinModificarZ_Initialize(object sender, EventArgs e)
        {
            try
            {
                String código;
                double distancia;
                AsignaParámetros(out código, out distancia);

                var líneasDeInterés = from entidad in DigiNG.DrawingFile
                                      where entidad is ReadOnlyLine
                                      where entidad.TieneElCódigo(código)
                                      let línea = entidad as ReadOnlyLine
                                      where línea.Perimeter > distancia
                                      select línea;

                foreach (var línea in líneasDeInterés)
                {
                    double últimaZ = línea.Points[0].Z;
                    double distanciaRecorridaConÚltimaZ = 0.0;

                    for (int i = 1; i < línea.Points.Count; i++)
                    {
                        if (línea.Points[i].Z == últimaZ)
                        {
                            distanciaRecorridaConÚltimaZ +=
                                Point2D.CalculateModule(línea.Points[i - 1], línea.Points[i]);

                            if (distanciaRecorridaConÚltimaZ > distancia)
                            {
                                Digi3D.Tasks.Add(new TaskGotoPoint(
                                    línea.Points[i - 1],
                                    Color.Aquamarine,
                                    6,
                                    "Línea con un tramo largo sin modificar su coordenada Z",
                                    TaskSeverity.Error,
                                    DigiNG.DrawingFile.Path,
                                    "detectar_lineas_con_tramos_largos_sin_modificar_z"));
                                break;
                            }
                        }
                        else
                        {
                            últimaZ = línea.Points[i].Z;
                            distanciaRecorridaConÚltimaZ = 0.0;
                        }
                    }

                }

            }
            catch (Exception)
            {
            }
            finally
            {
                Dispose();
            }
        }

        private void AsignaParámetros(out string código, out double distancia)
        {
            if (this.Args.Length != 2)
            {
                Digi3D.Music(MusicType.Error);
                Digi3D.ShowBallon(
                    "Detector de líneas con tramos sin modificar Z",
                    "No has indicado los parámetros.nFormato: [código] [distancia]",
                    2,
                    BallonIcon.Error);
                throw new Exception();
            }

            código = this.Args[0];

            if (!double.TryParse(this.Args[1], out distancia))
            {
                Digi3D.Music(MusicType.Error);
                Digi3D.ShowBallon(
                    "Detector de líneas con tramos sin modificar Z",
                    "El segundo parámetro no indica una distancia válida",
                    2,
                    BallonIcon.Error);
                throw new Exception();
            }
        }
    }
}

Pero lo podemos mejorar más aún, pues las tareas pueden tener a su vez subtareas, y estaría muy bien que esta tarea tuviera como subtarea una del tipo TaskAnimateEntity para que el usuario sepa la línea en la que se ha detectado el error (podría suceder que en esa ubicación se localicen muchas líneas.

A continuación el recorte de código correspondiente para añadir esa funcionalidad:

                             ...

                            if( distanciaRecorridaConÚltimaZ > distancia )
                            {
                                ITask[] subtareas = new[] {
                                    new TaskAnimateEntity(
                                        línea,
                                        2,
                                        "Animar la línea",
                                        TaskSeverity.Error)
                                };

                                Digi3D.Tasks.Add(new TaskGotoPoint(
                                    línea.Points[i - 1],
                                    Color.Aquamarine,
                                    6,
                                    "Línea con un tramo largo sin modificar su coordenada Z",
                                    TaskSeverity.Error,
                                    DigiNG.DrawingFile.Path,
                                    "detectar_lineas_con_tramos_largos_sin_modificar_z",
                                    subtareas));

                           ...

y como una imagen vale más que mil palabras, aquí tienes una captura de la tarea con subtareas:

Captura de pantalla del panel de tareas con una tarea con subtareas

Tarea que selecciona una entidad y además desplaza la cámara (sin cambiar el factor de zoom) a una determinada posición

De todos modos podemos obtener un comportamiento parecido utilizando el tipo Digi21.DigiNG.TaskEntityGotoPoint que es una mezcla de los tipos
TaskGotoPoint y TaskAnimateEntity

Su constructor mas completo es el siguiente:

        public TaskEntityGotoPoint(
            Point3D coordinates,
            Color color,
            int size,
            Entity entity,
            int seconds,
            string title,
            TaskSeverity severity,
            string drawingFile,
            string module,
            ITask[] childs);

que no requiere de ninguna explicación.

Aquí tienes el recorte de nuestra orden utilizando este tipo:

                            if (distanciaRecorridaConÚltimaZ > distancia)
                            {
                                Digi3D.Tasks.Add(new TaskEntityGotoPoint(
                                    línea.Points[i - 1],
                                    Color.Red,
                                    6,
                                    línea,
                                    2,
                                    "Línea con un tramo largo sin modificar su coordenada Z",
                                    TaskSeverity.Error,
                                    DigiNG.DrawingFile.Path,
                                    "detectar_líneas_con_tramos_sin_modificar_z"));

                                DigiNG.RenderScene();
                                break;
                            }

Crea tu propia tarea

Si con las tareas que te proporciona el framework no tienes suficiente, siempre eres libre de implementar tu propia tarea.
Lo único que tienes que hacer crear un tipo que implemente el interfaz Digi21.Digi3D.ITask.

Para verlo en acción, vamos a crear un tipo de tarea que activa únicamente la visualización de un grupo de códigos, de manera que si el usuario hace doble clic, se ocultarán el resto de códigos, para que el usuario se centre únicamente en el código o grupos de códigos que han desencadenado que aparezca la tarea en la barra de tareas.

    public class TareaActivarSoloCódigosDeEntidad : ITask
    {
        string[] códigos;

        public TareaActivarSoloCódigosDeEntidad(Entity entidad)
        {
            this.códigos = (from código in entidad.Codes select código.Name).ToArray();
        }

        public ITask[] Childs
        {
            get { return null; }
        }

        public string DrawingFile
        {
            get { return ""; }
        }

        public void Execute()
        {
            DigiNG.Codes.ShowCode( nombreCódigo => códigos.Contains(nombreCódigo) );
            DigiNG.RenderScene();
        }

        public string Module
        {
            get { return ""; }
        }

        public TaskSeverity Severity
        {
            get { return TaskSeverity.Error; }
        }

        public string Title
        {
            get { return "Activar únicamente los códigos de esta entidad"; }
        }
    }

y podemos añadir tareas de este tipo como subtareas de las tareas del ejemplo anterior:

                            if( distanciaRecorridaConÚltimaZ > distancia )
                            {
                                TareaActivarSoloCódigosDeEntidad[] subtareas = new[] {
                                    new TareaActivarSoloCódigosDeEntidad(línea)
                                };

                                Digi3D.Tasks.Add( new TaskEntityGotoPoint(
                                    línea.Points[i-1],
                                    Color.Aquamarine,
                                    6,
                                    línea,
                                    2,
                                    "Línea con un tramo largo sin modificar su coordenada Z",
                                    TaskSeverity.Error,
                                    DigiNG.DrawingFile.Path,
                                    "detectar_lineas_con_tramos_largos_sin_modificar_z",
                                    subtareas));
                                break;
                            }