Aprendizaje de Autolisp 2006
Aprendizaje de Autolisp 2006
Aprendizaje de Autolisp 2006
Aprendizaje de AutoLISP
Información general
Aprendizaje de AutoLISP proporciona lecciones paso a paso en
las que se enseña cómo utilizar Visual LISP™ para desarrollar
una aplicación de AutoLISP®. En Aprendizaje de AutoLISP se Archivos de ayuda
incluyen los siguientes temas:
Manual de personalización
z Introducción AutoLISP Reference
AutoLISP Developer’s Guide
z Diseño e inicio del programa
Aprendizaje de AutoLISP
z Uso de las herramientas de depuración de Visual LISP Manual de referencia de DXF
Manual del desarrollador de ActiveX y
z Dibujo del contorno del camino VBA
z Creación de un proyecto y adición del interface ActiveX and VBA Reference
Sheet Set Objects Reference
z Dibujo de las baldosas Connectivity Automation Reference
z Funcionamiento de los reactivos ObjectARX
Mi Ayuda
z Integración de todos los elementos
Recursos en la Web
Este manual de aprendizaje está diseñado para mostrar las diversas ventajas del entorno de
programación AutoLISP® para AutoCAD® y presentar las funciones del lenguaje AutoLISP con las que
el usuario puede que no esté familiarizado.
El objetivo de este manual de aprendizaje consiste en dibujar un camino de jardín con ayuda de una
herramienta de dibujo automatizada que reduce al mínimo el tiempo de dibujo y muestra las ventajas de
la programación paramétrica. Aprenderá a crear una rutina de dibujo que automatiza la generación de
formas complejas, con el fin de no tener que repetir el mismo diseño una y otra vez.
¿Comentarios?
Introducción
Este manual de aprendizaje está diseñado para usuarios con experiencia en AutoCAD y que estén
familiarizados con LISP o AutoLISP. Asimismo, se presupone un conocimiento de las tareas básicas de
administración de archivos de Windows® , como crear carpetas, copiar archivos y desplazarse por el
z Introducción del entorno Visual LISP® (VLISP) Este entorno proporciona herramientas de edición,
depuración, etc. diseñadas para crear aplicaciones de AutoLISP.
z Se presentan las funciones de ActiveX® y Reactor de AutoLISP, así como otras ampliaciones del
lenguaje AutoLISP que se incluyen con VLISP.
z Esta aplicación se puede ejecutar como LISP interpretado en archivos independientes y en funciones
cargadas en un solo documento.
z De manera alternativa, el código del programa se puede compilar en una aplicación VLX, que genera
un archivo ejecutable con extensión *.vlx. Los ejecutables VLX funcionan desde un espacio de
nombre autónomo que interactúa con el documento que carga la aplicación
¿Comentarios?
Introducción
El objetivo de este manual de aprendizaje consiste en desarrollar un comando nuevo para AutoCAD
que dibuje un camino de jardín y lo rellene con baldosas circulares. El aprendizaje se divide en siete
ejercicios. A medida que se avanza, las instrucciones sobre cómo efectuar tareas individuales se
ofrecen de forma menos detallada. Si tiene alguna duda, puede encontrar ayuda en la documentación
de VLISP.
Los ejercicios 4 y 5 son de nivel intermedio y exigen el conocimiento de los conceptos básicos de
AutoLISP. Los ejercicios 6 y 7 contienen tareas de programación avanzadas y de considerable
complejidad, y están diseñados para desarrolladores de AutoLISP con experiencia.
Si ha seleccionado la instalación completa al instalar AutoCAD, los archivos de código fuente estarán
en el siguiente directorio:
Directorio de <AutoCAD>\Tutorial\VisualLISP\
Si no ha instalado los ejemplos durante la instalación AutoCAD, vuelva a ejecutar la instalación, elija
Personalizada y, a continuación, seleccione el elemento Aprendizaje.
No es recomendable modificar los archivos de código fuente de ejemplo suministrados con AutoCAD. Si
el programa que está desarrollando no funciona correctamente, puede resultarle útil copiar el código
fuente de ejemplo en el directorio de trabajo. En este manual de aprendizaje, el directorio de trabajo se
denomina:
Directorio de <AutoCAD>\Tutorial\VisualLISP\Micamino
Si elige una ruta distinta para el directorio de trabajo, sustituya este directorio cuando sea necesario.
¿Comentarios?
En este primer ejercicio empezará definiendo qué debe hacer la aplicación. En el entorno de desarrollo
Visual LISP® (VLISP) creará un archivo LISP y empezará a escribir el código de AutoLISP® necesario
para la aplicación. Mientras tanto, descubrirá cómo VLISP facilita el desarrollo de las aplicaciones.
z Repaso de la lección 1
¿Comentarios?
z Trazar un contorno rectilíneo a partir de un punto inicial, un punto final y una anchura. El contorno
puede situarse en cualquier orientación 2D. No debe especificarse un límite de tamaño mínimo ni
máximo.
z Solicitar al usuario valores para el tamaño de las baldosas y el espacio entre las mismas. Las
baldosas son sencillos círculos, que rellenan el contorno sin sobrepasarlo ni solaparse con él.
z Colocar las baldosas en filas alternas.
Si desea ver cómo funciona en la práctica, puede ejecutar una versión completa de la aplicación que se
facilita con AutoCAD.
¿Comentarios?
Ahora que ha visto cómo debe funcionar la aplicación, puede empezar a desarrollarla con VLISP. Pero
antes es conveniente comprobar lo que puede ocurrir cuando VLISP espera la devolución del control de
AutoCAD. Es posible que ya haya experimentado esta situación.
Para comprobar cómo Visual LISP espera la devolución del control de AutoCAD
1. En el menú Herramientas de AutoCAD, seleccione Cargar aplicación.
2. Seleccione gardenpath.vlx en el directorio Tutorial\VisualLISP y elija Cargar.
3. Seleccione Cerrar.
4. En la solicitud de comando de AutoCAD, escriba vlisp para iniciar Visual LISP.
5. Vuelva a la ventana de AutoCAD (seleccione AutoCAD en la barra de tareas o pulse ALT + TAB y
seleccione AutoCAD) y, a continuación, escriba gpath en la solicitud de comando de AutoCAD.
6. Antes de responder a las solicitudes de gpath, vuelva a la ventana de VLISP.
En la ventana de VLISP el puntero del ratón tiene el aspecto de un símbolo de VLISP y no
es posible elegir ningún comando ni introducir texto en ninguna parte de esta ventana. El
símbolo del puntero sirve para recordar que, antes de reanudar el trabajo con VLISP, es
necesario terminar una actividad en AutoCAD. Recuerde esto siempre que vea el puntero
de VLISP.
7. Vuelva a la ventana de AutoCAD y responda a todas las solicitudes de gpath.
3. En el menú Archivo , seleccione Guardar como y guarde el código del nuevo archivo en el
directorio de <AutoCAD\Tutorial\VisualLISP\Micamino\gpmain.lsp.
4. Revise el trabajo realizado.
¿Comentarios?
VLISP reconoce los diversos tipos de caracteres y palabras que constituyen un archivo de programa de
AutoLISP y resalta los caracteres en distintos colores. Esto facilita una rápida identificación de los
errores. Por ejemplo, si faltan las comillas finales después de una cadena de texto, todo lo que se
escribe a continuación sigue mostrándose en magenta, el color que identifica las cadenas. Cuando se
introducen las comillas cerradas, VLISP aplica el color correcto al texto siguiente a la cadena, según el
elemento del lenguaje que represente.
A medida que se introduce el texto, VLISP le aplica el formato adecuado, agregando espacios y
sangrías. Para que VLISP asigne formato al código copiado en su editor de texto desde otro archivo, en
el menú VLISP seleccione Herramientas Formatear código en editor.
¿Comentarios?
La secuencia de función defun define la nueva función. Observe que la función principal se llama
C:GPath. El prefijo C: establece que es posible llamar a esta función desde la línea de comando de
AutoCAD. GPath es el nombre que introduce el usuario para iniciar la aplicación desde la solicitud de
comando de AutoCAD. Las funciones que obtienen entradas de los usuarios se llaman gp:getPointInput
y gp:getDialogInput. La función que dibuja el contorno del camino de jardín es gp:drawOutline. Estos
nombres van precedidos de gp: para indicar que son específicos de la aplicación del camino de jardín.
No es obligatorio, pero se recomienda utilizar este prefijo para distinguir las funciones específicas de
una aplicación de las funciones de utilidad general que se emplean con frecuencia.
En la función principal, las expresiones princ, muestran el resultado del programa si se ejecuta
adecuadamente o un mensaje de advertencia si surge un imprevisto. Por ejemplo, como se verá en el
Ejercicio 2, si el usuario pulsa INTRO en vez de designar un punto de la pantalla, la llamada a
gp:getPointInput finaliza de forma prematura y devuelve un valor nil a la función principal. Como
consecuencia, el programa emitirá un mensajeprinc que indicará "Incomplete information to draw a
boundary".
La llamada a princ cerca del final del programa hace las veces de solicitud. Cuando se carga la
aplicación, la solicitud comunica al usuario lo que debe escribir para dar comienzo al dibujo de un
camino de jardín. El último princ sin argumento de cadena obliga al programa a finalizar sin respuesta,
esto es, sin devolver el valor de la última expresión de la función principal. Si se omitiera este último
princ inhibidor, la solicitud aparecería dos veces.
¿Comentarios?
Para que el código de este archivo nuevo funcione correctamente, debe escribir otras tres definiciones
de función. El código principal del camino de jardín contiene llamadas a tres funciones personalizadas:
z gp:getPointInput
z gp:getUserInput
z gp:drawOutline
Por ahora sólo vamos a escribir definiciones de funciones vacías. Las funciones vacías sirven de
marcadores para las funciones completas que se escribirán posteriormente. Permiten experimentar con
fragmentos de código antes de agregar todos los detalles necesarios para completar la aplicación.
Antes del final de cada función de entrada hay una línea de código que contiene sólo una T. Se utiliza
como valor de devolución para la función que la llama. Todas las funciones de AutoLISP devuelven un
valor a la función que las llama. La letra T simboliza "true" (verdadero) en AutoLISP y cuando se agrega
esta letra, la función devuelve un valor verdadero. Tal y como se ha estructurado gpmain.lsp, cada
función de entrada a la que llama debe devolver un valor distinto de nil (que equivale a "ningún valor")
para que el programa continúe con el paso siguiente.
Por defecto, las funciones de AutoLISP devuelven el valor de la última expresión que hayan evaluado.
En las funciones vacías, la única expresión es una llamada a la función alert Pero alert siempre
devuelve nil. Si se deja como última expresión de gp:getPointInput, siempre devolverá nil, y nunca
pasará a la función if para llegar a gp:getDialogInput.
Por un motivo parecido, el final de la función gp:DrawOutline devuelve un símbolo precedido de una
comilla ('SomeEname) como marcador. Un símbolo entre comillas es una construcción LISP que no se
ha evaluado. (Si siente curiosidad por saber cómo funciona el lenguaje LISP; existe un gran número de
libros, mecionados al final de este tutorial.
¿Comentarios?
VLISP cuenta con una útil herramienta para buscar errores de sintaxis en el código. Utilícela antes de
intentar ejecutar el programa. Permite identificar errores mecanográficos frecuentes, como la falta de
paréntesis o comillas y otros problemas sintácticos.
Si necesita ayuda, consulte la sección “Introducción del entorno Visual LISP” de AutoLISP Developer’s
Guide. Intente averiguar dónde se encuentra el problema. Si tarda demasiado tiempo en localizar el
problema, utilice el archivo de muestra gpmain.lsp, que se encuentra en el directorio lesson1, para
continuar con el aprendizaje.
¿Comentarios?
La ejecución de programas de AutoLISP en VLISP permite utilizar las múltiples funciones de depuración
de VLISP para investigar los problemas que pueden surgir en la aplicación.
Nota Si AutoCAD se encuentra minimizado al ejecutar gpath, no se podrán ver las solicitudes hasta que
se restablezca la ventana de AutoCAD (por medio de la barra de tareas o ALT + TAB).
¿Comentarios?
En esta lección:
Este ejercicio ha terminado. Vuelva a guardar el archivo del programa para asegurarse de que contiene
las últimas modificaciones.
¿Comentarios?
En este capítulo aprenderá a utilizar las distintas herramientas de depuración de Visual LISP® para
hacer más rápido el desarrollo de los programas AutoLISP®. También aprenderá la diferencia entre las
variables locales y globales, y cuándo utilizarlas. El programa se comportará de forma más activa y
pedirá a los usuarios que introduzcan información. La información se almacenará en una lista y
empezará a entender la utilidad de las listas dentro de los programas de AutoLISP. A fin de cuentas,
LISP recibe este nombre por ser un lenguaje que procesa listas
z Repaso de la Lección 2
¿Comentarios?
En este ejercicio se trata el uso de las variables locales comparadas con las variables de documento
globales. Puede acceder a las variables globales desde todas las funciones cargadas en un documento
(un dibujo de AutoCAD®). Estas variables pueden conservar su valor después de que finalice el
programa que las ha definido. En ocasiones, esto es lo deseable. Más adelante, en este ejercicio, se
presenta un ejemplo.
Las variables locales sólo conservan su valor mientras se ejecuta la función que las ha definido.
Cuando la función termina de ejecutarse, los valores de la variable local se desechan automáticamente,
y el sistema vuelve a tener a su disposición la memoria que utilizaba la variable. Esto se conoce como
recuperación automática de la memoria no usada y es una función de la mayoría de los entornos de
desarrollo LISP, como VLISP. Las variables locales utilizan la memoria con más eficacia que las
globales.
Otra importante ventaja consiste en que las variables locales facilitan la depuración y el mantenimiento
de las aplicaciones. Con las variables globales no se sabe nunca cuándo ni en qué función se puede
modificar su valor; mientras que en las variables locales la detección de errores es más fácil.
Normalmente se producen menos efectos secundarios (es decir, cuando una parte del programa afecta
a una variable de otra parte del programa).
A causa de las ventajas mencionadas, en este aprendizaje se utilizan casi exclusivamente variables
locales.
Nota Si ha utilizado AutoLISP concierta frecuencia, probablemente estará acostumbrado a utilizar las
variables globales para examinar el programa que está desarrollando. Esto ya no es necesario, gracias
a las completas herramientas de depuración de VLISP.
¿Comentarios?
(defun gp:getPointInput()
(alert
"Function gp:getPointInput will get user drawing input"
)
;; For now, return T, as if the function worked correctly.
T
)
Por el momento, no sirve para gran cosa. Ahora empezaremos a construirla, agregándole funciones
para pedir datos al usuario, que definirá el punto inicial, el punto final y la anchura del camino.
Cuando la función gp:getPointInput está completa, las variables y los valores que tienen asignados
dejan de existir. Por tanto, los valores introducidos por el usuario se almacenan en variables locales.
Éste es el aspecto que puede presentar la función:
Las variables locales se declaran a continuación de la barra oblicua, en la secuencia defun que da
comienzo a la función. La primera llamada a getpoint solicita al usuario que indique un punto inicial. A
continuación, el punto final se calcula en relación con el punto inicial elegido. Mientras selecciona el
punto final, el usuario observará una línea elástica que se extiende desde el punto inicial. Del mismo
modo, al establecer el valor de la mitad de la anchura, el usuario podrá ver otra línea elástica, esta vez
representando la distancia, que surge del punto final.
¿Comentarios?
Se necesita una forma de transmitir los valores de una función a otra. Una manera de conseguirlo
consiste en crear una lista de los valores recuperados de gp:getPointInput, como se indica en el código
siguiente:
Copie esta versión de gp:getPointInput en la ventana de consola y pulse INTRO. Ahora dispone de la
oportunidad de utilizar otra característica de la ventana de consola.
La función devuelve una lista que contiene dos listas anidadas y un valor real (de coma flotante). Los
valores devueltos son parecidos a los siguientes:
¿Comentarios?
El ejemplo anterior funciona, pero se puede mejorar. En el siguiente ejercicio se va a crear una lista de
asociaciones o lista assoc (llamada así por la función de LISP que gestiona las listas de asociaciones).
En las listas de asociaciones los valores que nos interesan se asocian a valores clave. He aquí un
ejemplo:
((10 4.46207 4.62318 0.0) (11 7.66688 4.62318 0.0) (40 . 1.018248))
En la lista de asociaciones de ejemplo, los valores clave son los números 10, 11 y 40. Estos valores
clave son el índice unívoco de la lista. Es el mecanismo que utiliza AutoCAD para devolver datos de
entidades a AutoLISP cuando accede a una entidad desde su programa. El valor clave 10 indica un
punto inicial, y el 11 se utiliza normalmente para el punto final.
Tal vez se pregunte cuáles son las ventajas de las listas de asociaciones. Para empezar, a diferencia
de las listas normales, el orden de los valores devueltos no tiene importancia. Observe de nuevo la
primera lista:
Los valores devueltos no indican qué sublista es el punto inicial ni cuál es el punto final. Además, si la
función se modifica posteriormente, cualquier otra función que dependa de los datos devueltos en un
orden determinado puede verse afectada negativamente.
Si se utiliza una lista de asociaciones, el orden de los valores no tiene importancia. Si cambia el orden
de una lista de asociaciones se puede saber de todas formas qué define cada valor. Por ejemplo, un
valor 11 sigue siendo un punto final, independientemente del lugar que ocupe en la lista general.
¿Comentarios?
Cuando se utilizan listas de asociaciones se debe documentar qué representan los valores clave. En el
Camino de jardín, los valores clave 10, 11, 40, 41 y 50 significan lo siguiente:
A continuación se muestra una versión actualizada de la función gp:getPointInput. En ella, una función
de AutoLISP llamada cons (abreviatura de "construir lista") crea las sublistas con claves que pertenecen
a la lista de asociaciones. Copie esta versión en la ventana de consola, pulse INTRO y ejecute
(gp:getPointInput) de nuevo:
Recuerde que al construir la lista, el programa convierte la mitad de la anchura especificada por el
usuario en la anchura total multiplicando el valor por 2.
_$ (gp:getPointInput)
((10 2.16098 1.60116 0.0) (11 12.7126 7.11963 0.0) (40 . 0.592604) (50 . 0.4818
_$
¿Comentarios?
Ahora vamos a intentar otra cosa. Vuelva a llamar a la función, pero esta vez, almacenará el valor
devuelto en una variable llamada gp_PathData. Para ello, escriba lo siguiente en la solicitud de la
ventana de consola:
Para ver el valor de la variable que acaba de definir, escriba su nombre en la solicitud de la ventana de
consola:
_$ gp_PathData
((10 2.17742 1.15771 0.0) (11 13.2057 7.00466 0.0) (40 . 1.12747) (50 . 0.48749
¿Comentarios?
VLISP presenta el valor de la variable en una sola línea de la ventana Inspección (la
ventana básica mostrada en la ilustración). En este caso, el valor de la variable es una lista
muy larga, que no se puede ver entera. Se puede cambiar de tamaño la ventana
Inspección arrastrando su marco, pero hay una alternativa mejor.
2. En la ventana Inspección, haga doble clic en el nombre de la variable. Se abre una ventana
Inspeccionar:
4. Cuando termine de inspeccionar las variables, cierre todas las ventanas Inspeccionar, pero deje
abierta la ventana Inspección.
¿Comentarios?
Ahora que ha visto cómo utilizar las listas de asociaciones en código AutoLISP, puede utilizar este
método para escribir la versión completa de la función gp:getPointInput. Utilice el código siguiente para
sustituir o modificar la versión de gp:getPointInput que guardó anteriormente en gpmain.lsp.
Nota Si necesita o desea escribir el código en gpmain.lsp, en vez de copiarlo de otro archivo, puede
ahorrar tiempo si omite los comentarios (todas las líneas que empiezan con un punto y coma). No
obstante, no es aconsejable acostumbrarse a escribir código sin comentarios.
;;;--------------------------------------------------------------;
;;; Function: gp:getPointInput ;
;;;--------------------------------------------------------------;
;;; Description: This function asks the user to select three ;
;;; points in a drawing, which will determine the ;
;;; path location, direction, and size. ;
;;;--------------------------------------------------------------;
;;; If the user responds to the get functions with valid data, ;
;;; use startPt and endPt to determine the position, length, ;
;;; and angle at which the path is drawn. ;
;;;--------------------------------------------------------------;
;;; The return value of this function is a list consisting of: ;
;;; (10 . Starting Point) ;; List of 3 reals (a point) denoting ;
;;; ;; starting point of garden path. ;
;;; (11 . Ending Point) ;; List of 3 reals (a point) denoting ;
;;; ;; ending point of garden path. ;
;;; (40 . Width) ;; Real number denoting boundary ;
;;; ;; width. ;
;;; (41 . Length) ;; Real number denoting boundary ;
;;; ;; length. ;
;;; (50 . Path Angle) ;; Real number denoting the angle ;
;;; ;; of the path, in radians. ;
;;;--------------------------------------------------------------;
(defun gp:getPointInput(/ StartPt EndPt HalfWidth)
(if (setq StartPt (getpoint "\nStart point of path: "))
Si está copiando y pegando el código, agregue los siguientes comentarios como cabecera, antes de
C:GPath:
;;;**************************************************************;
;;; Function: C:GPath The Main Garden Path Function ;
;;;--------------------------------------------------------------;
;;; Description: This is the main garden path function. It is a ;
;;; C: function, meaning that it is turned into an ;
;;; AutoCAD command called GPATH. This function ;
;;; determines the overall flow of the garden path ;
;;; program. ;
;;;**************************************************************;
;;; The gp_PathData variable is an association list of the form: ;
;;; (10 . Starting Point) - List of 3 reals (a point) denoting ;
;;; starting point of the garden path. ;
;;; (11 . Ending Point) - List of 3 reals (a point) denoting ;
;;; endpoint of the garden path. ;
;;; (40 . Width) - Real number denoting boundary ;
;;; width. ;
;;; (41 . Length) - Real number denoting boundary ;
;;; length. ;
;;; (50 . Path Angle) - Real number denoting the angle of ;
;;; the path, in radians. ;
;;; (42 . Tile Size) - Real number denoting the size ;
;;; (radius) of the garden path tiles. ;
;;; (43 . Tile Offset) - Spacing of tiles, border to border. ;
;;; ( 3 . Object Creation Style) ;
;;; - Object creation style indicates how ;
;;; the tiles are to be drawn. The ;
;;; expected value is a string and one ;
;;; one of three values (string case ;
;;; is unimportant): ;
;;; "ActiveX" ;
;;; "Entmake" ;
;;; "Command" ;
;;; ( 4 . Polyline Border Style) ;
;;; - Polyline border style determines ;
;;; the polyline type to be used for ;
;;; path boundary. The expected value ;
;;; one of the following (string case is;
;;; unimportant): ;
;;; "Pline" ;
;;; "Light" ;
;;;**************************************************************;
¿Comentarios?
VLISP considera comentarios todas las declaraciones de AutoLISP que empiezan por punto y coma.
Los dos últimos ejemplos de código contenían muchos comentarios. Los comentarios del código de
AutoLISP se escriben como aclaración para el desarrollador, y no influyen en la ejecución del programa.
Es conveniente incluir siempre comentarios en el código. Tal vez se pregunte por qué.
z Suponga que dentro de nueve meses tiene que modificar el programa para añadirle funciones que
los usuarios echan en falta. Es posible que no recuerde exactamente lo que ha hecho, y la secuencia
de funciones que ahora le parece más clara le puede parecer en el futuro una serie de paréntesis
irreconocible.
z Además, los comentarios también sirven para explicar el código a otras personas encargadas de
actualizar el programa. Leer el código de otra persona puede resultar muy frustrante, sobre todo si
VLISP contiene varias utilidades que pueden ayudar a comentar el código. Observe que algunos de los
comentarios del ejemplo empiezan con tres puntos y coma (;;;), otros con dos (;;) y otros sólo con uno
(;). Para ver cómo VLISP gestiona los distintos comentarios, consulte “Applying Visual LISP Comment
Styles” en el AutoLISP Developer’s Guide.
Para ahorrar espacio y no malgastar papel, los siguientes ejemplos de código de este aprendizaje no
incluyen todos los comentarios que contienen los archivos de código fuente de muestra. Se presupone
que ya ha adquirido la conveniente costumbre de añadir comentarios y lo hará sin que se lo
recordemos.
¿Comentarios?
Los puntos de interrupción son símbolos (puntos) que se colocan en el código fuente para indicar dónde
se desea que se deje de ejecutar temporalmente un programa. Cuando se ejecuta el código, VLISP
continúa con normalidad hasta llegar a un punto de interrupción. En ese punto, VLISP suspende la
ejecución y espera a que el usuario le diga lo que tiene que hacer. No se detiene el programa
definitivamente, únicamente se mantiene en un estado de animación suspendida.
¿Comentarios?
La barra de herramientas de depuración se divide en tres grupos, cada uno de los cuales tiene tres
botones. Cuando se ejecuta un programa en modo depuración la barra de herramientas presenta este
aspecto:
z Los tres primeros botones permiten avanzar por el código del programa.
z Los tres botones siguientes permiten determinar la forma en que VLISP debe continuar la ejecución
después de detenerse en un punto de interrupción o en un error.
z Los tres botones siguientes sirven para colocar o eliminar un punto de interrupción, agregar un
Inspección y saltar a la posición del código fuente en la que se produjo la última interrupción
2. Haga clic una vez. Esta posición se muestra en la siguiente instantánea de pantalla:
3. Con el punto de inserción definido, pulse el botón Act/Des punto interrupción en la barra de
herramientas Depurar.
El botón Act/Des punto interrupción es un conmutador que alterna entre los estados de
activado y desactivado. Si no hay un punto de interrupción en la posición del cursor, lo
añade; si ya hay uno, lo elimina.
4. Pulse el botón Cargar ventana de edición activa en la barra Herramientas para cargar el archivo.
z El cursor se encuentra justo en el punto de interrupción. Esto puede ser difícil de ver, por lo que
VLISP proporciona otra pista.
z En la barra de herramientas de depuración, el icono indicador de pasos presenta un cursor en I rojo
delante de unos paréntesis. Esto indica que el depurador de VLISP está detenido antes de la
expresión.
¿Comentarios?
Hay tres botones que se pueden utilizar para revisar el código. Se trata de los tres iconos que se
encuentran en la parte izquierda de la barra de herramientas de depuración. Representan, por orden,
Antes de pulsar uno de estos botones, observe de nuevo el estado del código resaltado, la posición del
cursor y el botón indicador de pasos. En resumen: se resalta un expresión, que consiste en anidar una
función getdist en una función setq y colocar el cursor justo al principio del bloque resaltado.
Nota Durante este ejercicio, si se pulsa el botón incorrecto y se salta un paso o dos es muy fácil volver
a empezar. En primer lugar, pulse el botón Restablecer de la barra de herramientas de depuración. Esto
pone fin a la ejecución del código de VLISP y restablece el sistema VLISP al nivel superior. A
continuación, vuelva a empezar por el primer paso.
Ahora está resaltada la primera función cons, y VLISP se detiene justo antes de la función (observe el
botón indicador de pasos).
¿Comentarios?
Mientras se avanza por un programa es posible agregar variables a la ventana Inspección y cambiar su
valor.
Si la ventana Inspección contiene aún la variable gp_PathData, pulse el botón Borrar ventana que se
encuentra en la parte superior de la ventana.
Si una variable no contiene el valor que considera que le corresponde, puede cambiarlo y ver cómo
influye sobre el programa. Por ejemplo, supongamos que espera que el valor halfwidth sea un número
entero. Sin embargo, como no fue preciso al designar los puntos durante la selección de entradas
acabó con un valor de 1,94818.
Observe que el valor de la ventana Inspección cambia. Sin embargo, esto no garantiza que
se vaya a utilizar el valor nuevo en la creación de la sublista de anchura (40 . width) en la
lista de asociaciones. Agregue otra expresión a la ventana Inspección para comprobarlo.
2. En el menú de VLISP elija Depurar Inspeccionar última evaluación.
Esto agrega una variable llamada *Last-Value* a la ventana Inspección. <:imk 127)
*Last‑Value* es una variable global en la que VLISP guarda automáticamente el valor de
la última expresión evaluada.
3. Avance por el programa (pulsando Entrar o Pasar al siguiente) hasta que se evalúe la expresión
encargada de crear la sublista width. El código de esta acción es:
(cons 40 (* HalfWidth 2.0))
¿Comentarios?
Queda por explicar otro punto: ¿qué ocurre con el valor de las variables locales de gp:getPointInput al
salir de la función?.
Examine los valores de las variables de la ventana Inspección. Dado que son variables locales de la
función gp:getPointInput, el valor de endpt y StartPt es nil. VLISP ha vuelto a ocupar automáticamente
la memoria que ocupaban estas variables. Normalmente, HalfWidth, la tercera variable local de la
función, también tiene como valor nil, pero a causa de la actividad de depuración, se ha sustituido
globalmente en la ventana de consola y sigue teniendo un valor 2.0 en la ventana Inspección. También
la variable global *Last-Value* presenta la lista de asociaciones creada por gp:getPointInput.
Acaba de finalizar su primera sesión de depuración. Pero no olvide que el programa sigue en animación
suspendida.
¿Comentarios?
En esta lección:
Las herramientas que ha aprendido a utilizar en este ejercicio formarán parte de su trabajo cotidiano si
pretende desarrollar aplicaciones de AutoLISP con LISP.
¿Comentarios?
En este ejercicio va a ampliar el programa para poder dibujar algo en AutoCAD: el contorno de la
polilínea del camino de jardín. Para dibujar el borde es necesario crear funciones de utilidades que no
pertenecen a una sola aplicación, sino que son de índole general y se podrán reutilizar más adelante.
También va a aprender a escribir funciones que aceptan argumentos (datos que se transmiten a la
función desde fuera) y por qué el uso de argumentos es muy importante en programación. Cuando
termine con este ejercicio, dibujará una figura de AutoCAD de forma paramétrica, esto decir, dibujará de
forma dinámica una figura basada en los parámetros facilitados exclusivamente por el usuario.
z Repaso de la lección 3
¿Comentarios?
Las funciones de utilidades llevan a cabo tareas comunes a muchas de las aplicaciones que escribirá.
Estas funciones constituyen un conjunto de herramientas que se podrá utilizar una y otra vez.
Cuando se crea una función como parte de un conjunto de herramientas es conveniente invertir cierto
tiempo en su documentación detallada. También es aconsejable incluir en los comentarios las
características que se desea agregar a la función en el futuro.
¿Comentarios?
Ahora va a crear una función que evita tener que escribir de forma repetitiva una ecuación. Tiene el
siguiente aspecto:
Tal vez se pregunte para qué necesita una función que convierte medidas angulares. Internamente,
AutoCAD® utiliza radianes para medir los ángulos, aunque es más habitual pensar en grados. Esta
función del conjunto de herramientas permite pensar en grados y hace que AutoLISP ® los convierta a
radianes.
Para utilizar esta función dentro del programa basta con copiar su definición de la ventana de consola
en el archivo gpmain.lsp. Se puede pegar en cualquier lugar del archivo, siempre y cuando no se sitúe
dentro de otra función.
Para limpiar el trabajo, seleccione el texto que acaba de pegar y pulse el botón Formatear selección;
VLISP aplicará al código las sangrías y el formato adecuados.
;;;--------------------------------------------------------------;
;;; Function: Degrees->Radians ;
;;;--------------------------------------------------------------;
;;; Description: This function converts a number representing an ;
;;; angular measurement in degrees, into its radian ;
;;; equivalent. There is no error checking on the ;
;;; numberOfDegrees parameter -- it is always ;
;;; expected to be a valid number. ;
;;;--------------------------------------------------------------;
(defun Degrees->Radians (numberOfDegrees)
(* pi (/ numberOfDegrees 180.0))
)
¿Comentarios?
Otra función muy útil del programa de camino de jardín convierte los puntos 3D en puntos 2D.
Normalmente, AutoCAD trabaja con coordenadas 3D, pero algunas entidades, como las polilíneas
ligeras, son siempre 2D. Los puntos que devuelve la función getpoint son 3D, por lo que es necesario
crear una función que los convierta.
Esto funciona, pero hay que tener en cuenta otra cosa para la aplicación de camino de
jardín. Aunque a menudo es indiferente que los números sean enteros o reales en las
funciones de LISP, no ocurre lo mismo con las funciones de ActiveX, que se utilizan más
adelante en este ejercicio. Las funciones de ActiveX requieren números reales. Es fácil
modificar la función para que devuelva números reales en lugar de enteros.
3. Escriba el código siguiente en la solicitud de la consola:
(defun 3dPoint->2dPoint (3dpt)(list (float(car 3dpt))
(float(cadr 3dpt))))
Observe que los valores devueltos ahora son reales (como indican los valores decimales).
5. Vuelva a comprobar la función, utilizando esta vez la función getpoint. Escriba lo siguiente en la
solicitud de la consola:
(setq myPoint(getpoint))
¿Comentarios?
La mayoría de los programas AutoLISP dibujan las entidades por medio de uno de estos métodos:
z Funciones ActiveX
z La función entmake
z La función command
¿Comentarios?
La forma más avanzada de crear entidades consiste en utilizar las funciones de ActiveX dentro de
VLISP. ActiveX tiene varias ventajas sobre entmake y command.
¿Comentarios?
La función entmake permite crear entidades recopilando valores tales como la posición y la orientación
de coordenadas, la capa y el color en una lista de asociaciones, y solicitando posteriormente a
AutoCAD que genere la entidad. La lista de asociaciones que se crea para la función entmake es muy
parecida a la lista de asociaciones que se obtiene cuando se llama a la función entget. La diferencia es
que entget devuelve la información sobre una entidad, pero entmake crea una entidad nueva a partir de
los datos introducidos.
¿Comentarios?
Cuando AutoLISP apareció en AutoCAD, sólo era posible crear entidades por medio de la función
command. Esta función permite a los programadores de AutoLISP incluir en el código prácticamente
cualquiera de los comandos que es posible ejecutar desde la línea de comandos de AutoCAD. Es un
método fiable, pero no es tan rápido como los métodos de ActiveX, y no cuenta con la flexibilidad de
entmake.
¿Comentarios?
;;;--------------------------------------------------------------;
;;; Function: gp:drawOutline ;
;;;--------------------------------------------------------------;
;;; Description: This function draws the outline of the ;
;;; garden path. ;
;;;--------------------------------------------------------------;
(defun gp:drawOutline ()
(alert
(strcat "This function will draw the outline of the polyline "
"\nand return a polyline entity name/pointer."
)
)
;; For now, simply return a quoted symbol. Eventually, this
;; function will return an entity name or pointer.
'SomeEname
)
Tal y como está, el código no hace gran cosa. Sin embargo, la lista de asociaciones guardada en la
variable gp_PathData proporciona suficiente información para calcular los puntos del contorno del
camino. Ahora debe averiguar la forma de transmitir la información de esta variable a gp:drawOutline.
Recuerde que gp_PathData es una variable local definida dentro de la función C:GPath. En AutoLISP,
las variables locales declaradas en una función son visibles desde cualquier función a la que se llame
desde ésta (si desea más explicaciones, consulte Diferencia entre variables locales y globales). Se llama
a la función gp:drawOutline desde dentro de C:GPath. Puede consultar la variable gp-PathData de
Tal vez se pregunte por qué. Cuando dos funciones que utilizan la misma variable se definen en el
mismo archivo, como en los ejemplos mostrados hasta ahora, no es difícil saber dónde se define la
variable y para qué sirve. Sin embargo, si las funciones se definen en archivos distintos, como ocurre a
menudo, sería necesario realizar búsquedas en los dos archivos para averiguar qué representa
gp_PathData.
z de parámetros a funciones
¿Comentarios?
Una forma mejor de transferir información de una función a otra consiste en transmitir parámetros a la
función llamada. La función se debe diseñar de forma que pueda recibir varios valores. ¿Recuerda la
función Degrees->Radians? A esta función se le transmite un parámetro llamado numberOfDegrees:
Cuando se llama a la función, espera que se introduzca un número. El número que se encuentra junto a
Degrees->Radians se declara como el parámetro llamado numberOfDegrees. Por ejemplo:
_$ (degrees->radians 90)
1.5708
También es posible transmitir una variable a una función. Por ejemplo, tenemos una variable llamada
aDegreeValue que contiene el número 90. Los siguientes comandos establecen aDegreeValue y
transmiten la variable a Degrees->Radians:
1.5708
¿Comentarios?
(gp:drawOutline gp_PathData)
Es bastante sencillo, pero también debe averiguar la forma de procesar la información almacenada en
la lista de asociaciones. La función Inspección de VLISP puede resultar útil para dar el siguiente paso.
La función cdr devuelve el segundo elemento de la lista y todos los siguientes. En este
ejemplo, cdr recupera el valor del ángulo, que es el segundo y último elemento de la
entrada devuelta por la función assoc.
Después de ver lo anterior debe interpretar sin problemas el siguiente fragmento de código:
(setq PathAngle (cdr (assoc 50 BoundaryData))
¿Comentarios?
Todavía quedan un par de asuntos por tratar. En primer lugar hay que averiguar cómo dibujar el camino
con el ángulo especificado por el usuario. La función gp:getPointInput permite establecer fácilmente el
ángulo primario del camino. Para dibujarlo son necesarios un par de vectores adicionales
perpendiculares al ángulo primario.
Con los datos disponibles actualmente es posible determinar los puntos de las cuatro esquinas del
camino, por medio de la función polar:
La función polar devuelve un punto 3D a un ángulo y una distancia especificados respecto a un punto.
Por ejemplo, polar emplaza p2 proyectando p1 una distancia de PathLength a lo largo de un vector
orientado a un ángulo de PathAngle, en el sentido contrario a las agujas del reloj desde el eje x.
¿Comentarios?
La función gp:drawOutline efectúa llamadas de ActiveX para mostrar el marco de polilínea en AutoCAD.
El siguiente fragmento de código utiliza ActiveX para dibujar el marco:
Interpretemos este código: El ActiveX and VBA Reference, es un recurso esencial que describe los
métodos y propiedades, como esta aplicación de camino de jardín, a los que tienen acceso los clientes
de ActiveX. La sección “Trabajo con ActiveX” de AutoLISP Developer’s Guide explica cómo traducir la
sintaxis de VBA™ de ActiveX and VBA Reference en llamadas de ActiveX con sintaxis de AutoLISP.
No obstante, por el momento puede entenderlo provisionalmente examinando la estructura de las dos
llamadas a vla- del ejemplo anterior. El nombre de todas las funciones ActiveX de AutoLISP que
funcionan en los objetos de AutoCAD está precedido de vla-. Por ejemplo, addLightweightPolyline es el
nombre de un método de ActiveX, y vla-addLightweightPolyline es la función de AutoLISP que llama a
este método. La llamada a vla-put-closed actualiza la propiedad cerrada del objeto pline, la polilínea que
dibuja vla‑addLightweightPolyline.
Los objetos de automatización que influyen en las llamadas a ActiveX de AutoLISP se rigen por una
serie de normas:
z El primer argumento de una llamada a los métodos vla-put, vla-get, o vla- es el objeto que se
¿Comentarios?
Las funciones de ActiveX no se activan automáticamente al iniciar AutoCAD o VLISP, por lo que hay
que asegurarse de que los programas tengan cargado ActiveX. Esto se consigue con la siguiente
llamada de función:
(vl-load-com)
Si aún no está disponible ActiveX, la ejecución de vl-load-com inicia el entorno ActiveX de AutoLISP. Si
ActiveX ya se ha cargado, vl-load-com no tiene ningún efecto.
¿Comentarios?
Al añadir entidades mediante funciones ActiveX, tendrá que identificar el espacio modelo o el espacio
papel en que se debe insertar la entidad. (En la terminología ActiveX las entidades son objetos, pero en
este tutorial seguiremos utilizando el término entidad. Para comunicar a AutoCAD qué espacio deben
ocupar las entidades nuevas hay que conseguir un puntero a ese espacio. El problema es que la
obtención de un puntero al espacio modelo no es una tarea fácil. El siguiente fragmento de código
muestra la forma en que hay que configurar la operación:
(vla-get-ModelSpace (vla-get-ActiveDocument
(vlax‑get‑Acad‑Object)))
Ésta no es una expresión que se pueda escribir una y otra vez cómodamente. Por ejemplo, observe lo
complicado que es el código de adición de una polilínea por medio de ActiveX cuando se utiliza toda la
extensión del espacio modelo:
Sin duda, esta función es menos comprensible. Además, dentro de cada expresión del programa en la
que se crea una entidad se repite el mismo conjunto de funciones anidadas. Esto demuestra uno de los
pocos usos idóneos de las variables globales. La aplicación de Camino de jardín puede agregar
muchas entidades al espacio modelo (piense en todas las baldosas del camino), por lo que es
conveniente establecer una variable global que guarde el puntero al espacio modelo, como en el código
siguiente:
La variable *ModelSpace* se puede utilizar cada vez que se llame a una función de creación de
entidades de ActiveX. El único problema es que la variable *ModelSpace* debe estar preparada antes
de dar comienzo al dibujo. Por ello, es necesario llamar a la función setq que establece esta variable
cuando se cargar la aplicación, inmediatamente después de la llamada a vl-load-com. Estas llamadas
se colocarán delante de cualquier defun en el archivo del programa. Como consecuencia, se ejecutan
en cuanto se carga
¿Comentarios?
El último asunto que se va a tratar es la manera de convertir las variables de punto individuales (p1, p2,
p3, y p4) al formato necesario para la función vla‑addLightweightpolyline. En primer lugar, busque
ayuda sobre el tema.
The array of 2D WCS coordinates specifying the vertices of the polyline. At lea
Las variantes son construcciones de ActiveX que sirven de contenedores para varios tipos de datos.
Las cadenas, los enteros y las matrices se pueden representar por medio de variantes. Las variantes
almacenan datos junto con la información que los identifica.
Hasta ahora tenemos cuatro puntos, todos ellos en formato (x, y, z). Lo que tenemos que hacer es
convertir estos cuatro puntos en una lista con la siguiente estructura:
(x1 y1 x2 y2 x3 y3 x4 y4)
La función append toma varias listas y las concatena. Para crear una lista de los cuatro puntos en el
formato adecuado para la función de ActiveX se puede utilizar la expresión siguiente:
Escribir cuatro veces la función 3dPoint‑>2dPoint resulta un poco trabajoso. Puede reducir más el
código por medio de las funciones mapcar y apply. Una vez seleccionada, mapcar ejecuta una función
en elementos individuales de una o más listas y apply transfiere una lista de argumentos a la función
especificada. El código resultante tiene este aspecto:
Por último, después de aplicar la función append a la lista devuelta por mapcar, se obtiene lo siguiente:
(x1 y1 x2 y2 x3 y3 x4 y4)
¿Comentarios?
Hasta ahora, los datos de la variable de polipuntos se encuentran en un formato de lista adecuado para
muchas llamadas de AutoLISP. Sin embargo, estos datos se deben proporcionar como parámetro de
entrada a una llamada de ActiveX que espera una matriz de coordenadas de variante. Se puede usar
otra función de utilidades para efectuar la conversión necesaria, de lista a variante:
A continuación puede ver un ejemplo de una llamada a gp:list‑>variantArray para convertir una lista
en una matriz de coordenadas de variante:
¿Comentarios?
Tenga en cuenta que el código anterior se encuentra fuera de los grupos defun. Por ello,
VLISP ejecuta el código automáticamente cuando se carga el archivo.
4. Observe la siguiente línea de la función C:GPath:
Ahora, la función gp:drawOutline espera un parámetro (la lista que contiene los datos del
contorno de la polilínea) y este cambio cumple dicho requisito.
5. Agregue la función gp:list->variantArray que se muestra en Creación de una variante a partir de una
lista de puntos al final de gpmain.lsp.
Intente cargar y ejecutar el programa revisado. VLISP quita el control a AutoCAD antes de
que se vea el resultado final, por lo que se debe volver a la ventana de AutoCAD después
de que el control vuelva a VLISP. Si el programa se ha ejecutado correctamente, debe
aparecer un marco del camino de jardín. Si encuentra algún error, depure el código y
vuelva a intentarlo.
¿Comentarios?
En esta lección:
Si no ha comprendido del todo este ejercicio, es aconsejable que lo repase antes de empezar con el
Ejercicio 4 (si decide hacerlo, copie el código completo del directorio Lesson2 para comenzar al
ejercicio desde el lugar adecuado). Si todo lo demás falla, puede copiar el código correcto del directorio
Tutorial\VisualLISP\Lesson3.
¿Comentarios?
En este ejercicio se realizarán dos tareas principales: la creación de un proyecto Visual LISP® y la
adición de una interfaz de cuadro de diálogo a la aplicación. Para ello, dividirá el único archivo de
AutoLISP® con el que ha trabajado hasta ahora ( gpmain.lsp) en varios archivos más pequeños, en
aplicación del concepto de modularidad del código.
A partir de este ejercicio, el aprendizaje proporciona descripciones más generales de las tareas que
deben realizarse, excepto cuando se traten temas nuevos. Asimismo, los fragmentos de código se
comentan al mínimo para ahorrar espacio.
z Limpieza
z Ejecución de la aplicación
z Repaso de la Lección 4
¿Comentarios?
Como resultado del trabajo efectuado en el ejercicio 3, el archivo gpmain.lsp tiene un tamaño
considerable. Esto no es un problema para VLISP, pero el mantenimiento del código resulta más fácil si
se divide en archivos que contengan funciones relacionadas de forma lógica. También facilita la
depuración del código. Por ejemplo, si tiene un solo archivo con 150 funciones, puede resultar muy
difícil encontrar un paréntesis que falta.
Asimismo, al principio del archivo, inserte las líneas de código que activan la funcionalidad
El escritorio de VLISP empieza a llenarse mucho. Puede minimizar cualquier ventana de VLISP sin que
deje de ser accesible. En la barra de herramientas, pulse el botón Seleccionar ventana para elegir una
ventana de la lista o bien elija Ventana en el menú de VLISP y seleccione la ventana que desee
presentar.
¿Comentarios?
La función de proyectos de VLISP facilita la gestión de los archivos que constituyen la aplicación. Con
esta función es posible abrir a la vez todos los archivos de un proyecto, en vez de abrir uno a uno todos
los archivos LISP de la aplicación. Una vez abierto el proyecto, los archivos que lo constituyen están a
un doble clic de distancia
VLISP agrega una pequeña ventana de proyecto al escritorio de VLISP. En la ventana se muestra una
lista de los archivos del proyecto. Haga doble clic en cualquiera de ellos para abrirlo en el editor de texto
de VLISP (si no está abierto ya) y convertir el editor en la ventana activa
¿Comentarios?
La siguiente parte de este ejercicio trata sobre la forma de agregar una interfaz de cuadros de diálogo a
la aplicación de camino de jardín. Para esto hay que trabajar con otro lenguaje, llamado DCL ( Dialog
Control Language, Lenguaje de control de cuadros de diálogo).
En la actualidad, la función gpath sólo acepta entradas en la línea de comando. Hemos incluido una
función vacía (gp:getDialogInput) con intención de agregar una interfaz de cuadros de diálogo. Éste es
el momento de agregar la interfaz.
Para crear una interfaz funcional de cuadros de diálogo hay que dar dos pasos:
El código de programa que inicia la configuración por defecto y responde a la interacción del usuario se
agrega a gp:getDialogInput.
¿Comentarios?
Los elementos que constituyen un cuadro de diálogo se denominan componentes en DCL. Puede
parecer que escribir todo el contenido de un cuadro de diálogo en un archivo DCL supone mucho
trabajo. El truco consiste en esbozar lo que se desea, dividirlo en secciones y escribir las secciones una
a una.
Observe que se ha dado al botón de opción que lleva la etiqueta "optimizada" un valor de
1. El valor de 1 (como cadena; no como entero) asignado a un botón lo convierte en la
opción por defecto de su grupo. En otras palabras, cuando se abra el cuadro de diálogo
éste es el botón que se encontrará seleccionado. Observe también que, en los archivos
DCL, los comentarios seidentifican con dos barras oblicuas, y no con punto y coma como
en AutoLISP.
4. Defina la columna de selecciones para especificar el estilo de creación de entidades añadiendo el
siguiente código:
: boxed_radio_column { // defines the radio button areas
label = "Tile Creation Method";
: radio_button { // defines the ActiveX radio button
label = "&ActiveX Automation";
key = "gp_actx";
value = "1";
}
: radio_button { // defines the (entmake) radio button
label = "&Entmake";
key = "gp_emake";
}
: radio_button { // defines the (command) radio button
label = "&Command";
key = "gp_cmd";
}
}
5. Agregue el código siguiente para definir los componentes de la casilla de edición, que permiten a
los usuarios introducir los números que definen el tamaño y el intervalo de las baldosas:
: edit_box { // defines the Radius of Tile edit box
label = "&Radius of tile";
key = "gp_trad";
edit_width = 6;
}
: edit_box { // defines the Spacing Between Tiles edit box
label = "S&pacing between tiles";
key = "gp_spac";
edit_width = 6;
}
Observe que esta definición no establece ningún valor inicial para las casillas de edición.
Los valores por defecto de las casillas de edición se definen en el programa AutoLISP.
6. Agregue el código siguiente para los botones Aceptar y Cancelar:
: row { // defines the OK/Cancel button row
: spacer { width = 1; }
: button { // defines the OK button
label = "OK";
is_default = true;
key = "accept";
width = 8;
fixed_width = true;
}
: button { // defines the Cancel button
label = "Cancel";
is_cancel = true;
key = "cancel";
width = 8;
fixed_width = true;
}
: spacer { width = 1;}
}
Los dos botones están definidos en una fila, por lo que tienen alineación horizontal.
8. La directriz dialog requiere una llave de cierre, por lo que se debe ir al final del archivo y colocar
esta llave como última línea del código DCL:
}
¿Comentarios?
Antes de guardar el archivo que contiene el DCL se debe tener en cuenta que AutoCAD® debe ser
capaz de localizar este archivo durante la ejecución. Por este motivo, el archivo debe estar colocado en
una de las ubicaciones Ruta de búsqueda de archivo de soporte de AutoCAD. (Si no está seguro de
estas ubicaciones, en AutoCAD seleccione Herramientas Opciones y examine Ruta de búsqueda de
archivo de soporte en la ficha Archivos).
Observe que VLISP cambia el modelo de coloreado de la sintaxis después de guardar el archivo. VLISP
está diseñado para reconocer los archivos DCL y resaltar distintos tipos de elementos sintácticos.
¿Comentarios?
VLISP incluye una función de presentación preliminar que permite controlar el resultado del código
DCL.
En este caso, el archivo DCL sólo define un cuadro de diálogo, por lo que no es necesario
elegir. No obstante, cuando se crean aplicaciones mayores y más sólidas es normal que
los archivos DCL contengan varios cuadros de diálogo. Aquí es donde se elige el que se
desea ver.
3. Si el cuadro de diálogo se presenta correctamente, pulse cualquier botón para salir.
VLISP transmite el control a AutoCAD para presentar el cuadro de diálogo. Si AutoCAD encuentra
errores de sintaxis, muestra una o varias ventanas de mensaje en las que los identifica.
Si AutoCAD encuentra errores de DCL y no sabe cómo arreglarlos, copie el archivo gpdialog.dcl del
directorio Tutorial\VisualLISP\Lesson4 y guárdelo en el directorio Support.
¿Comentarios?
Ahora es necesario programar la función de AutoLISP de modo que interactúe con el cuadro de diálogo.
La actividad tendrá lugar en la función vacía gp:getDialogInput, es donde tendrá lugar la actividad. Esta
función se encuentra ahora en el archivo gp-io.lsp, ya que antes de extrajo de gpmain.lsp.
El desarrollo de una interfaz de cuadros de diálogo puede resultar muy complicado las primeras veces.
Para hacerlo es necesario planificar las tareas de antemano y plantearse preguntas como:
¿Comentarios?
Cuando se ejecuta la aplicación de camino de jardín completa, el cuadro de diálogo empieza siempre
con ActiveX como método por defecto de creación de objetos, y Optimizada como estilo de polilínea.
Con el tamaño de baldosa por defecto ocurre algo más interesante: los valores cambian según la
anchura del camino. El siguiente fragmento de código muestra la forma en que hay que configurar los
valores por defecto que se muestran en el cuadro de diálogo:
De momento, no se preocupe por la finalidad de las variables dialogLoaded y dialogShow. Esto se verá
en las dos secciones siguientes.
¿Comentarios?
El programa debe empezar por cargar el archivo DCL mediante la función load_dialog. Esta función
busca archivos de cuadro de diálogo atendiendo a la ruta de búsqueda de archivo de soporte de
AutoCAD, excepto si se especifica una ruta completa.
Cada vez que se coloque en el código una función load_dialog se debe poner más adelante la función
unload_dialog correspondiente. Ahora mismo veremos esto. Por ahora, observe cómo se carga el
cuadro de diálogo:
¿Comentarios?
Antes se ha comentado que un archivo DCL puede contener la definición de varios cuadros de diálogo.
El siguiente paso, pues, consiste en especificar cuál de las definiciones de cuadro de diálogo se debe
mostrar. Esto se muestra en el código siguiente:
Observe que la función and se utiliza para comprobar si se ha cargado el cuadro de diálogo, y si la
llamada a new_dialog se ha efectuado correctamente. Si hay varias expresiones evaluadas dentro de
una llamada de función and, la evaluación de las expresiones subsiguientes finaliza con la primera
expresión cuya evaluación arroje un valor de nil. En este caso, si el indicador de dialogLoaded es nil (lo
que significa que ha fallado la función de carga de la sección anterior), VLISP no intenta llevar a cabo la
función new_dialog.
Observe que el código también prevé la posibilidad de que algo no funcione correctamente en el archivo
DCL, y establece la variable dialogShow en nil si esto ocurre.
La función new_dialog sólo carga el cuadro de diálogo en la memoria, pero no lo muestra. La función
start_dialog muestra el cuadro de diálogo. Todas las tareas de inicialización del cuadro de diálogo,
como la definición de valores del componente, la creación de imágenes o listas para cuadros de lista y
la asociación de acciones a componentes específicos deben tener lugar después de la llamada a
new_dialog y antes de la llamada a start_dialog.
¿Comentarios?
Ahora hay que establecer los valores iniciales del radio de la baldosa y el intervalo entre baldosas. La
función set_tile asigna valores a los componentes. Las casillas de edición utilizan cadenas en lugar de
números, por lo que se debe emplear la función rtos (convertir número real en cadena) para convertir
los valores de las variables de tamaño de la baldosa en cadenas de formato decimal, con una precisión
de dos cifras. El código siguiente lleva a cabo esta conversión:
¿Comentarios?
Las definiciones de ACL se limitan a crear un cuadro de diálogo "sin vida". Este cuadro de diálogo se
conecta al código dinámico de AutoLISP con la función action_tile, de la forma expuesta en el código
siguiente:
Observe todas las comillas que rodean el código de AutoLISP. Cuando se escribe una función
action_tile de AutoLISP, el código da instrucciones al componente para que recuerde la cadena y se la
devuelva cuando el usuario elija el componente. La cadena (cualquier serie de caracteres entre comillas
dobles) permanecerá inactiva hasta que el usuario seleccione el componente. En ese momento, el
componente transmite la cadena a AutoCAD, que la convierte en código funcional de AutoLISP y
ejecuta el código.
Por ejemplo, consideremos la siguiente expresión action_tile, que está conectada al botón de opción de
la polilínea optimizada:
(action_tile
"gp_lw"
"(setq plineStyle \"Light\")"
)
El código asigna la cadena "(setq plineStyle \"Light\")" al botón de opción. Cuando un usuario elige este
botón, la cadena se devuelve a AutoCAD y se transforma directamente en la siguiente expresión de
AutoLISP:
Observe otro fragmento de código. Ésta es la expresión action_tile asignada al botón Aceptar:
(action_tile
"accept"
(strcat "(progn (setq tileRad (atof (get_tile \"gp_trad\")))"
"(setq tileSpace (atof (get_tile \"gp_spac\")))"
"(done_dialog) (setq UserClick T))"
)
Cuando un usuario pulsa el botón Aceptar, la cadena que tiene asignada se transmite a AutoCAD y se
transforma en el siguiente código de AutoLISP:
(progn
(setq tileRad (atof (get_tile "gp_trad")))
(setq tileSpace (atof (get_tile "gp_spac")))
(done_dialog)
(setq UserClick T)
)
Este código realiza varias acciones: recupera los valores actuales de los componentes cuyos valores
clave son gp_trad (el radio de la baldosa) y gp_spac (el valor del espacio entre baldosas). Después, atof
convierte la cadena numérica en un número real. El cuadro de diálogo se cierra con la función
done_dialog, y se asigna el valor T a la variable UserClick.
Ya ha terminado de asignar acciones a los botones. El siguiente paso consiste en poner esto en
marcha.
¿Comentarios?
La función start_dialog presenta un cuadro de diálogo y acepta los datos introducidos por el usuario. La
función start_dialog no requiere argumentos.
(start_dialog)
El control se transmite al usuario cuando se emite start_dialog. Los usuarios pueden efectuar
selecciones dentro del cuadro de diálogo hasta que pulsen Aceptar o Cancelar.
¿Comentarios?
Cuando un usuario pulse Aceptar o Cancelar, deberá descargar el cuadro de diálogo. Igual que
start_dialog, unload_dialog es una función muy sencilla.
(unload_dialog dcl_id)
¿Comentarios?
Cuando el usuario pulsa Aceptar, hay que crear una lista que contenga los valores establecidos por el
usuario en el cuadro de diálogo. Esta lista es lo que gp:getDialogInput devuelve a la función que la
llama. Si el usuario pulsa Cancelar, la función devuelve nil:
¿Comentarios?
Con los ejemplos anteriores y unas cuantas líneas adicionales tiene el código necesario para completar
la función gp:getDialogInput.
Integración de gp:getDialogInput
1. Abra su copia de gp-io.lsp en una ventana de edición de texto de VLISP.
2. Borre el código de gp:getDialogInput (la declaración defun gp:getDialogInput y todo lo que hay a
continuación).
3. Introduzca la siguiente declaración defun como primera línea de código de la función
gp:getDialogInput:
(defun gp:getDialogInput (pathWidth / dcl_id objectCreateMethod
plineStyle tilerad tilespace result UserClick
dialogLoaded dialogShow)
Nota Introduzca sólo el primer ejemplo de código de Asignación de acciones a los componentes no
los fragmentos de las explicaciones que se encuentran a continuación. Estos fragmentos sólo
repiten partes del ejemplo.
z Apertura del cuadro de diálogo
z Cierre del cuadro de diálogo
z Qué hacer a continuación
¿Comentarios?
z ¿Ha cambiado la declaración defun? Es decir, ¿aún toma la función el mismo número de
argumentos?
z ¿Devuelve la función algo diferente?
En el caso de gp:getDialogInput, la respuesta a las dos preguntas es sí. Ahora, la función acepta el
parámetro de la anchura del camino (para establecer el tamaño de baldosa y el intervalo entre baldosas
por defecto). En vez de devolver T, que es el valor que devolvía la versión vacía de la función,
gp:getDialogInput devuelve ahora una lista de asociaciones que contiene cuatro valores nuevos.
Los dos cambios influyen sobre el código que llama a la función y sobre el código que gestiona los
valores devueltos por las funciones. Sustituya la versión anterior de la función C:Gpath de gpmain.lsp
por el siguiente código:
Eche un vistazo a las líneas que aparecen en negrita en la revisión de la función C:GPath principal. Hay
dos cambios esenciales que hacen que el programa funcione correctamente:
z Cuando se llama a la función gp:getDialogInput se le transmite la anchura del camino. Esto se hace
extrayendo el valor asociado al índice de la clave 40 de la lista de asociaciones gp_PathData.
z La lista de asociaciones devuelta por gp:getPointInput se asigna a una variable llamada
gp_dialogResults. Si esta variable tiene un valor, su contenido debe agregarse al final de los valores
de la lista de asociaciones que ya se encuentran almacenados en gp_PathData.
En el código hay más cambios, que se deben a la sustitución de marcadores en la versión vacía de la
función. Lo más fácil es copiar este código del aprendizaje en pantalla y pegarlo en el archivo.
¿Comentarios?
Uno de los requisitos de la aplicación de camino de jardín era que los usuarios pudieran dibujar el
contorno como polilínea optimizada o polilínea al estilo antiguo. La primera versión de gp:drawOutline
que se escribió utilizaba siempre una polilínea optimizada para dibujar el contorno. Ahora que la interfaz
del cuadro de diálogo está preparada, es posible integrar la opción de dibujar también una polilínea de
estilo antiguo. Para ello, gp:drawOutline debe averiguar qué tipo de polilínea debe dibujar y, a
continuación, dibujarla.
Los cambios que se deben efectuar en gp:drawOutline se incluyen en el siguiente fragmento de código.
Efectúe la modificación desde el archivo gpdraw.lsp indicado en negrita:
Puede ser bastante complicado incorporar estos cambios al código, porque no sólo se deben agregar
partes, sino que es necesario borrar algunas líneas y reestructurar otras. Es recomendable copiar toda
la declaración setq del aprendizaje en pantalla y copiarla en el código.
¿Comentarios?
Ha estado utilizando este código como marcador, pero ahora que gp:drawOutline funciona, ya no es
necesario.
¿Comentarios?
Antes de ejecutar el programa, guarde todos los archivos modificados, si no lo ha hecho ya. Puede
elegir, en el menú de VLISP Archivo Guardar todo, o utilizar el método abreviado ALT+MAYÚS+s
para guardar todos los archivos abiertos.
Intente también dibujar el camino con polilíneas optimizadas y del estilo antiguo. Después de dibujar los
caminos, utilice el comando list de AutoCAD para comprobar si el programa está dibujando los tipos
correctos de entidades.
¿Comentarios?
En esta lección:
Ahora tiene un programa que dibuja un contorno de camino de jardín. En la próxima lección va a
agregar las baldosas al camino de jardín. En el proceso conocerá más herramientas de desarrollo de
programas de VLISP.
¿Comentarios?
Cuando termine con este ejercicio, la aplicación cumplirá los requisitos básicos establecidos en el
Ejercicio 1. Agregará la función de rellenar el camino con baldosas dentro del contorno y utilizará para
ello varios métodos diferentes de creación de entidades. También aprenderá el uso de varios métodos
abreviados y nuevas herramientas de edición.
z Repaso de la Lección 5
¿Comentarios?
Si no hay ninguna abierta, abra una copia de gpdraw.lsp en la ventana del editor de texto de Visual
LISP®. Este código tiene algunos aspectos que se repiten en gran parte del código que va a desarrollar
en VLISP. En primer lugar, encontrará varios paréntesis y otros que a su vez se encuentran entre
paréntesis. También existen muchas llamadas a funciones, algunas de ellas con nombres muy largos
(por ejemplo, vla‑addLightweightPolyline. En VLISP puede utilizar algunas herramientas que le
ayudarán a trabajar con estas funciones comunes del código AutoLISP®.
z Coincidencia de paréntesis
¿Comentarios?
VLISP cuenta con una función de comprobación de coincidencia de paréntesis que le ayudará a buscar
los paréntesis de cierre correspondientes a los de apertura.
VLISP busca el paréntesis de cierre correspondiente al que ha indicado y selecciona el código completo
entre los dos paréntesis. De este modo, además de asegurarse de haber incluido la cantidad correcta
de paréntesis, puede cortar o copiar el texto seleccionado más fácilmente. Estos consejos le habrán
sido de utilidad al actualizar esta llamada al final del Ejercicio 4.
¿Qué otras utilidades tiene? Puede copiar parte del código en la ventana Consola de VLISP, pegarlo y
hacer una prueba. También puede servir para reemplazar 50 líneas de código por otras tres mucho más
completas que acaba de crear. Con la herramienta de comprobación de coincidencia de paréntesis
puede seleccionar rápidamente el código antiguo y, a continuación, eliminarlo con sólo pulsar una tecla.
Es mucho más rápido dejar que VLISP busque el bloque completo que buscar todos los paréntesis de
cierre.
¿Comentarios?
Suponga que necesita añadir nuevas funciones a un programa mediante el código siguiente:
(No se preocupe por lo que pueda hacer este código, si es que hace algo. Es sólo un ejemplo que
incluye diversas variables largas y nombres de funciones.)
¿Comentarios?
Si ya ha trabajado con AutoLISP, posiblemente habrá tenido que escribir alguna expresión como la
siguiente:
A menudo, es complicado hacer un seguimiento de todas las funciones de selección definidas: ssname,
ssget, sslength, etc. La función Completar palabra por aproximación de VLISP puede ayudarle.
3. Borre el código.
¿Comentarios?
El código que añade una polilínea ligera al dibujo invoca una función denominada vla-
addLightweightPolyline. Además de ser una palabra muy larga para escribirla, existen muchas otras
funciones para crear entidades que también empiezan por vla-add. Si no desea consultar en el manual
el nombre de las funciones cada vez que va a crear un programa, puede dejar que lo haga VLISP.
6. Borre los cambios que ha hecho en el archivo gpdraw.lsp, ya que sólo sirven para esta
demostración. Cierre también los cuadros de diálogo Servicio de símbolos y Completar palabra
por aproximación.
¿Comentarios?
Ya tiene un contorno del camino y está listo para rellenarlo con baldosas. Necesitará aplicar algunos
datos lógicos y modificar una pequeña sección de la geometría.
¿Comentarios?
Una de las tareas que debe realizar es determinar el espacio entre las baldosas y dibujarlas. Si se trata
de un trazado sencillo de baldosas rectilíneas, puede uilizar el comando MATRIZ de AutoCAD® para
rellenar las baldosas. Sin embargo, para el camino de jardín necesitará desfasar cada fila de baldosas
de la fila anterior.
Este patrón de desfase de filas es un programa repetitivo. Imagne cómo sería la distribución de las
baldosas en la generación del camino real. Seguramente, trataría de empezar en un extremo para
seguir colocando las baldosas hasta que no quede más espacio libre.
¿Comentarios?
Para dibujar el camino de jardín sólo necesita conocer algunas cotas. La media anchura es fácil: es
exactamente la mitad de la anchura del camino. Ya ha definido el código para que los usuarios puedan
obtener esta anchura y lo ha guardado en una lista de asociación.
El espaciado de las baldosas también es sencillo: es el doble del radio, es decir, el diámetro, más el
espacio entre las baldosas. Las cotas también las eligen los usuarios.
El espaciado de las filas es algo más complejo, a menos que domine la trigonometría. Vea la fórmula a
continuación:
¿Comentarios?
Compruebe si la siguiente función tiene algún sentido. Compárela con el pseudo código e intente seguir
los cálculo geométricos descritos. Quizá encuentre algunas funciones de AutoLISP que desconoce. Si
necesita ayuda sobre alguna de las funciones, consulte la AutoLISP Reference. De momento, sólo
tiene que leer el código sin escribir nada.
(polar rowStartPoint
(setq rowStartPoint
(/ TileRadius 2.0)
) ;_ end of polar
) ;_ end of setq
;; Draw each row of tiles.
(while (<= SpaceFilled SpaceToFill)
;; Get the list of tiles created, adding them to our list.
(setq tileList (append tileList
(gp:calculate-Draw-TileRow
(setq rowStartPoint
(polar rowStartPoint
pathAngle
RowSpacing
) ;_ end of polar
) ;_ end of setq
TileRadius
TileSpace
pathWidth
pathAngle
offsetFromCenter
ObjectCreationStyle
) ;_ end of gp:calculate-Draw-TileRow
) ;_ end of append
;; Calculate the distance along the path for the next row.
RowSpacing)
SpaceFilled (+ SpaceFilled RowSpacing)
;; (causes alternate rows to be indented).
offsetFromCenter
(if (= offsetFromCenter 0.0)
offsetDistance
0.0
) ;_ end of if
) ;_ end of setq
) ;_ end of while
;; Return the list of tiles created.
tileList
) ;_ end of defun
Para compensar el desplazamiento hacia adelante inicial de rowStartPoint al dibujar la primera fila (es
decir, el primer ciclo durante el bucle while), necesita desplazar ligeramente rowStartPoint en la
dirección opuesta. Se trata de evitar un aspecto de un margen grande de espacio vacío entre el
contorno del camino y la primera fila. La mitad de TileRadius es una cantidad suficiente para desplazar
el punto. Para hacerlo, puede utilizar polar para proyectar rowStartPoint sobre un vector orientado a 180
grados desde PathAngle. Observe que el punto se coloca de forma provisional fuera del contorno del
camino.
El siguiente fragmento (modificado para hacerlo más comprensible) es quizá algo más complicado:
Es el conmutador de desplazamiento, que determina si la fila que se está dibujando debe empezar con
un círculo centrado en el camino o con un desfase de éste. El pseudo código de este algoritmo es el
siguiente:
¿Comentarios?
Ahora que ya dispone del código lógico para dibujar el camino, debe decidir cómo dibujar las baldosas
en cada fila. El siguiente diagrama muestra dos supuestos: una fila en la que el desfase desde el centro
del camino es igual a 0.0 y otra en la que es distinto Examine el diagrama y lea el pseudo código que
sigue.
¿Comentarios?
(setq ObjectCreationFunction
(cond
((equal ObjectCreationStyle "ACTIVEX")
gp:Create_activeX_Circle
)
((equal ObjectCreationStyle "ENTMAKE")
gp:Create_entmake_Circle
)
((equal ObjectCreationStyle "COMMAND")
gp:Create_command_Circle
)
(T
(alert
(strcat
"ObjectCreationStyle in function gp:calculate-Draw-TileRow"
"\nis invalid. Contact developer for assistance."
"\n ObjectCreationStyle set to ACTIVEX"
) ;_ end of strcat
) ;_ end of alert
(setq ObjectCreationStyle "ACTIVEX")
)
) ;_ end of cond
) ;_ end of setq
¿Recuerda la especificación que permite dibujar las baldosas (círculos) en ActiveX, con la función
entmake o con la función command? La variable ObjectCreationFunction tiene asignada una función de
tres posibles, dependiendo del parámetro ObjectCreationStyle (pasado desde C:GPath y mediante
gp:Calculate-and-Draw-Tiles). A continuación se muestran las tres funciones como se van a definir en
gpdraw.lsp:
La primera función dibuja un círculo utilizando una función ActiveX y devuelve un objeto ActiveX.
La segunda función dibuja un círculo utilizando entmake. Devuelve un nombre de entidad convertido en
un objeto ActiveX.
La tercera función dibuja un círculo utilizando command. También devuelve un nombre de entidad
convertido en un objeto ActiveX.
¿Comentarios?
¿Comentarios?
Ha empezado este ejercicio aprendiendo las funciones de edición de VLISP que pueden ayudarle a
Al completar el ejercicio, ha generado el código que dibuja las baldosas del camino de jardín. Ahora
tiene un programa que cumple los requisitos establecidos al principio del aprendizaje.
Alcanzado este punto, probablemente ha adquirido la experiencia necesaria en VLISP para continuar
por sus propios medios. No obstante, si lo desea, puede continuar con los dos ejercicios de aprendizaje
restantes, donde se explica cómo utilizar las funciones de reactor y otras funciones avanzadas del
entorno VLISP.
¿Comentarios?
En este ejercicio va a aprender qué son los reactivos y cómo utilizarlos para dibujar eventos y
entidades. Los reactivos permiten que AutoCAD® informe a la aplicación siempre que tengan lugar un
determinados eventos. Por ejemplo, si un usuario desplaza una entidad a la que la aplicación ha
enlazado un reactivo, la aplicación recibirá la notificación del desplazamiento de la entidad. Esto se
puede programar para desencadenar operaciones adicionales, como mover otras entidades asociadas
a la que ha desplazado el usuario, o para actualizar una etiqueta de texto que registra la información de
versión de la función de dibujo alterada. Es como dotar a la aplicación de un buscapersonas y decir a
AutoCAD que avise a la aplicación cuando ocurra algo.
z Repaso de la Lección 6
¿Comentarios?
Los reactivos son objetos que se enlazan al editor del dibujo o a determinadas entidades de un dibujo.
El objeto reactivo es como una alarma automática que sabe cómo llamar a su buscapersonas cuando
ocurre algo importante. El aviso lo envía una función de AutoLISP® a la que llama el reactivo, función
denominada retorno de llamada.
Nota La complejidad del código de la aplicación y el nivel de conocimientos necesarios para estos dos
últimos ejercicios son muy superiores a los de los ejercicios 1 a 5. Se facilita mucha información, pero
no se explica con tanto detalle como en los ejercicios anteriores. Si es principiante, no se preocupe si
no lo entiende la primera vez. Considérelo un vistazo a algunas de las útiles pero técnicamente difíciles
características de Visual VLISP®.
z Tipos de reactivos
¿Comentarios?
Existen varios tipos de reactivos de AutoCAD®. Cada tipo de reactivo responde a uno o más eventos de
AutoCAD. Los reactivos se agrupan en las siguientes categorías:
Reactivos de editor
Notifican a la aplicación cada vez que se llama a un comando de AutoCAD.
Reactivos de vinculador
Notifican a la aplicación cada vez que se carga o se descarga una aplicación ObjectARX®
Reactivos de base de datos
Corresponden a entidades u objetos determinados de una base de datos de dibujo.
Reactivos de documento
Notifican a la aplicación en modo MDI de los cambios en el documento de dibujo actual, como la
Con la excepción de los reactivos de editor, sólo hay un tipo de reactivo por categoría. Los reactivos de
editor incluyen una amplia gama de tipos: por ejemplo, los reactivos DXF™ (que notifican a una
aplicación acerca de cuándo se importa o se exporta un archivo DXF) y los reactivos de ratón (que
notifican sobre eventos de ratón como el doble clic).
Dentro de las categorías de reactivos hay muchos eventos distintos a los que se puede enlazar un
reactivo. AutoCAD permite realizar muchos tipos de acciones distintas, no obstante, el usuario deberá
elegir las acciones que más le convengan. Cuando termine, puede añadir el reactivo “auto-dialer” al
evento y, a continuación, escribir la función callback que se activa cuando se produce el evento.
¿Comentarios?
Para implementar las funciones de los reactivos en la aplicación camino de jardín, empiece por
gestionar unos pocos eventos, en vez de intentar abarcar todas las acciones que puede efectuar el
usuario.
¿Comentarios?
z Cuando se cambia la posición de una esquina (vértice) del contorno del camino de jardín, el camino
se debe dibujar de nuevo para que el contorno siga siendo rectilíneo. Además hay que volver a
dibujar las baldosas de acuerdo con el tamaño y la forma nuevos.
z Cuando se borra el contorno del camino de jardín se deben borrar también las baldosas.
¿Comentarios?
Para cada evento de reactivo se debe planificar la función a la que se llama cuando ocurre este. El
siguiente pseudocódigo esboza la secuencia lógica de eventos que debe tener lugar cuando el usuario
arrastra uno de los vértices de la polilínea a una posición nueva:
Defun gp:outline-changed
Erase the tiles.
Determine how the boundary changed.
Straighten up the boundary.
Redraw new tiles.
End function
Aquí hay un pequeño problema. Cuando el usuario empieza a arrastrar el contorno de un vértice de
polilínea, AutoCAD notifica a la aplicación emitiendo un evento :vlr‑modified. No obstante, el usuario
acaba de empezar a arrastrar uno de los vértices de la polilínea. Si se llama inmediatamente a la
función gp:outline‑changed, se interrumpe la acción que está efectuando el usuario. No se puede
conocer la nueva posición del vértice, porque el usuario no la ha seleccionado aún. Además, AutoCAD
no permite a la función modificar el objeto de polilínea mientras el usuario lo está arrastrando. AutoCAD
tiene el objeto de polilínea abierto para su modificación y lo deja abierto hasta que el usuario termina de
cambiar su posición.
Es necesario utilizar una variable global para identificar la polilínea que ha modificado el usuario, porque
no hay continuidad entre las funciones gp:outline-changed y gp:command-ended. En la aplicación,
estas dos funciones son llamadas y ejecutadas por separado. La variable global almacena información
importante que se establece en una función y a la que la otra debe tener acceso
Considere ahora qué hacer si el usuario borra el contorno del camino de jardín. El objetivo, en definitiva,
consiste en borrar todas las baldosas. El pseudocódigo siguiente esboza la lógica:
¿Comentarios?
Los usuarios pueden tener varios caminos de jardín en la pantalla y borrar más de uno. Es necesario
prever esta posibilidad
El reactivo asociado a una entidad es un reactivo de objeto. Si hay varias entidades en el dibujo,
también puede haber varios reactivos de objeto, uno para cada entidad. Un evento de edición
determinado, como el comando borra puede desencadenar muchos retornos de llamada, según cuántas
de las entidades seleccionadas tengan enlazado un reactivo. Los reactivos de editor, sin embargo, son
únicos. La aplicación sólo debe enlazar un reactivo de evento :vlr-commandEnded.
La secuencia de eventos para las dos modificaciones (el cambio de la posición del vértice y el borrado
de la polilínea) terminan con acciones que se deben efectuar dentro de la función gp:command-ended.
Determine qué conjunto de acciones se debe efectuar para cada condición. El pseudocódigo siguiente
esboza la lógica:
¿Comentarios?
El siguiente paso en la planificación de una aplicación con reactivos consiste en averiguar cómo y
cuándo se deben enlazar. Es necesario enlazar dos reactivos de objeto para el marco de la polilínea de
todos los caminos de jardín (uno para responder a los eventos de modificación y el otro para responder
al borrado), y un reactivo de editor para notificar a la aplicación cuando el usuario termina de modificar
la polilínea. Los reactivos de objeto están enlazados a entidades, mientras que los reactivos de editor
están registrados en AutoCAD.
Se debe tener en cuenta otra cosa. Para calcular de nuevo el contorno de la polilínea y devolverle la
forma rectilínea después de que el usuario lo haya modificado es necesario saber cuál era la
configuración del vértice antes de la modificación. Esta información no se puede determinar después de
modificar la polilínea. En ese momento sólo se puede obtener información sobre la configuración nueva.
¿Cómo resolver este problema? Podría mantener esta información en una variable global, pero esto
tiene un inconveniente. Los usuarios pueden dibujar todos los caminos de jardín que quieran, y todos
los caminos nuevos requerirían una nueva variable global.
¿Comentarios?
Defun C:GPath
Do everything that is already done in the garden path
(and don't break anything)
Attach an object reactor to the polyline using these parameters:
A pointer to the polyline just drawn,
A list of data that you want the reactor to record,
A list of the specific polyline object events to be tracked,
along with the LISP callback functions to be invoked
End of the object reactor setup
Attach editor reactor to the drawing editor using the
following parameters:
Any data you want attached to the reactor (in this case, none)
A list of the specific editor reactor events to be tracked,
along with the LISP callback functions to be invoked
End of the editor reactor setup
End function
¿Comentarios?
)
;; The following code removes all reactors when the drawing is
;; closed. This is extremely important!!!!!!!!!
;; Without this notification, AutoCAD may crash upon exiting!
(if (not *DrawingReactor*)
(setq *DrawingReactor*
(VLR-DWG-Reactor
nil ; No data is associated with the drawing reactor
'((:vlr-beginClose . gp:clean-all-reactors)
)
) ;_ end of vlr-DWG-reactor
)
)
) ;_ end of progn
(princ "\nFunction cancelled.")
) ;_ end of if
(princ "\nIncomplete information to draw a boundary.")
) ;_ end of if
(princ); exit quietly
) ;_ end of defun
;;; Display a message to let the user know the command name.
(princ "\nType GPATH to draw a garden path.")
(princ)
2. Repase las modificaciones del código y los comentarios que describen qué hace cada declaración
nueva. Este manual de aprendizaje muestra todo el código modificado en negrita.
¿Comentarios?
Las funciones de retorno de llamada de reactivo añaden bastante código a la aplicación. Puede
encontrar este código en el directorio Lesson6.
Lea los comentarios del archivo para entender lo que está haciendo. Observe que todas las funciones
de retorno de llamada son funciones vacías; lo único que hacen cuando se desencadenan es mostrar
una advertencia.
La última función del archivo es tan importante que merece un apartado propio.
¿Comentarios?
Los reactivos son muy activos. Cuando se diseña una aplicación que los utiliza es muy fácil que el
programa tenga errores, y a veces, que se produzcan también en AutoCAD. Es útil tener una
herramienta que elimine todos los reactivos agregados en caso necesario.
El archivo gpreact.lsp contiene una función llamada gp:clean-all-reactors que no hace gran cosa por sí
misma. Sin embargo, llama a la función CleanReactors. Añada esta función al archivo utils.lsp copiando
el siguiente código al final:
;;;--------------------------------------------------------------;
;;; Function: CleanReactors ;
;;;--------------------------------------------------------------;
;;; Description: General utility function used for cleaning up ;
;;; reactors. It can be used during debugging, as ;
;;; well as cleaning up any open reactors before ;
;;; a drawing is closed. ;
;;;--------------------------------------------------------------;
(defun CleanReactors ()
(mapcar 'vlr-remove-all
'(:VLR-AcDb-reactor
:VLR-Editor-reactor
:VLR-Linker-reactor
:VLR-Object-reactor
)
)
)
¿Comentarios?
Ahora ya debe tener en su sitio todas las piezas necesarias para experimentar en vivo con los reactivos.
ve nada interesante.
3. Intente hacer lo siguiente después de dibujar el camino:
z Mueva un vértice de la polilínea. Seleccione la polilínea para activar sus pinzamientos y
arrastre un vértice a otra posición.
z Estire la polilínea.
z Desplace la polilínea.
z Borre la polilínea.
Examine los mensajes que aparecen. Está observando las acciones que tienen lugar entre bastidores.
(Si la aplicación no funciona correctamente y no quiere tomarse la molestia de depurarla ahora, puede
ejecutar el código de ejemplo que se encuentra en el directorio Tutorial\VisualLISP\Lesson6. Utilice el
proyecto Gpath6 de ese directorio.)
Nota A causa del comportamiento del reactivo, puede observar que después de probar una secuencia
de reactivos en AutoCAD no es posible volver a VLISP pulsando ALT + TAB, ni haciendo clic para
activar la ventana de VLISP. Si esto ocurre, basta con que escriba vlisp en la solicitud de comando de
AutoCAD para volver a VLISP.
¿Comentarios?
Tome unos papeles y empiece a apuntar en ellos los eventos de reactivo que ocurren dentro de la
aplicación. Éste es un ejemplo de las cosas que debe buscar:
Dibuje diez caminos de jardín y después efectúe un seguimiento de las siguientes combinaciones
comando / objeto, seleccionando las polilíneas de forma sucesiva:
Este ejercicio le permitirá comprender qué es lo que ocurre entre bastidores. Si en algún momento del
Ejercicio 7 se desorienta con las funciones de los reactivos, consulte las notas de seguimiento que ha
tomado en papel.
¿Comentarios?
En esta lección se han presentado los reactivos de AutoCAD y ha aprendido a implementarlos con
VLISP. Ha diseñado un plan para agregar reactivos a la aplicación de camino de jardín y ha añadido
parte del código que el programa necesita para llevar a cabo el plan.
Los reactivos pueden añadir muchas funciones a las aplicaciones, pero recuerde: cuanto más complejo
sea un programa, más fácil será que tenga un fallo.
Otra cosa que se debe tener en cuenta es que, tal y como está diseñada la aplicación, las funciones de
los reactivos no se conservan de una sesión de dibujo a la siguiente. Si se guarda un dibujo que
contiene un camino de jardín enlazado a reactivos, éstos no estarán la próxima vez que se abra el
dibujo. Puede aprender a añadir reactivos permanentes consultando el tema “Transient versus
Persistent Reactors” del Visual LISP Developer’s Guide, y después leyendo sobre las funciones
mencionadas en la AutoLISP Reference.
¿Comentarios?
Deberá considerar esta parte del manual de aprendizaje como la sección de temas avanzados. Si es
principiante, es posible que no entienda todo el código de AutoLISP® que se presenta aquí. Al final de
este ejercicio se facilita una lista de libros sobre AutoLISP que proporcionan información más completa
sobre algunos de los conceptos avanzados de AutoLISP utilizados aquí.
¿Comentarios?
En este ejercicio deberá definir varias funciones nuevas. En vez de facilitarle detalles sobre todos los
aspectos del código nuevo, este ejercicio proporciona una visión general y señala los conceptos que
hay detrás del código. Al final del ejercicio tendrá todo el código fuente necesario para crear una
aplicación de camino de jardín idéntica al programa de muestra que ejecutó en el ejercicio 1.
Nota Cuando se desarrollan y se depuran aplicaciones con reactivos siempre existe la posibilidad de
desestabilizar AutoCAD®. Esto puede ocurrir, por ejemplo, si no se elimina un reactivo de las entidades
borradas. Por este motivo se recomienda cerrar VLISP guardando todos los archivos abiertos y salir de
AutoCAD para volver a abrir las dos aplicaciones antes de empezar con el Ejercicio.
Empiece por cargar el proyecto tal y como estaba al final del Ejercicio 6.
En la aplicación del camino de jardín quedan por hacer dos cosas fundamentales:
También debe considerar la forma de gestionar las variables globales del programa. A menudo es
conveniente que las variables globales retengan un valor a lo largo de toda la sesión de dibujo de
AutoCAD. Sin embargo, en el caso de los reactivos no es así. Para entenderlo, imagine que un usuario
de la aplicación Camino de jardín ha dibujado varios caminos en un solo dibujo. Después los borra,
primero uno a uno, después dos a dos, etc., hasta que sólo queda uno.
La primera vez que el usuario borra una polilínea todo funciona de la forma esperada. En gp:outline-
erased se guarda un puntero al reactor. Cuando se desencadena gp:command-ended se eliminan
también las baldosas asociadas a la polilínea a la que está enlazado el reactor, y todo marcha bien.
Entonces, el usuario decide borrar dos caminos. Como resultado, la aplicación recibe dos llamadas a to
gp:outline‑erased, na para cada polilínea que se va a borrar. Hay que prever dos posibles problemas:
z Cuando se establece con setq la variable *reactorsToRemove* se debe añadir a la variable global un
puntero a un reactivo, con cuidado de no sustituir los valores que se encuentran almacenados. Esto
significa que *reactorsToRemove* debe tener estructura de lista para que se pueda agregar punteros
Ésta es la secuencia de eventos que debe suceder para que los usuarios borren dos caminos de jardín
con un solo comando de borrado. Observe cómo se gestionan las variables globales:
Además de la variable global *reactorsToRemove*, la aplicación también incluye una variable global
*polyToChange* que almacena un puntero a cada polilínea que se va a modificar. Más adelante, en
esta lección, se introducirán dos variables globales adicionales para la aplicación.
¿Comentarios?
Cuando se escribe una aplicación con reactivos, es necesario gestionar cualquiera de los comandos
que afecten de forma significativa a los objetos. Una de las actividades de diseño del programa debería
ser la de revisar todos los posibles comandos de edición de AutoCAD y determinar cómo la aplicación
deberá responder ante cada uno de ellos. El formato de la hoja de seguimiento de reactivos que se
muestra casi al final de la lección 6 es un buen ejemplo. Llame a aquellos comandos que crea que el
usuario va a utilizar y anote el tipo de comportamiento con el que debería responder la aplicación.
También deberá planificar las siguientes acciones:
z Determinar qué hacer cuando los usuarios activen los comandos DESHACER y REHACER
z Determinar qué hacer cuando los usuarios activen un comando UY después de borrar entidades
vinculadas a reactivos.
Para evitar que un tema complicado pueda convertirse en un tema muy muy complicado, este
aprendizaje no pretende cubrir todas las posibilidades que debería, y esta lección se ocupa de una
parte mínima de esta funcionalidad.
Aunque no vaya a desarrollar toda la funcionalidad de estos comandos adicionales, compruebe lo que
unas cuantas funciones de edición adicionales supondrían:
z Si los usuarios estiran el contorno de una polilínea (mediante el comando ESTIRA), pueden ocurrir
varias cosas. Se puede estirar en cualquier dirección, no sólo en la dirección del eje mayor o del eje
menor, de manera que el contorno podría adquirir una forma muy extraña. Además, es necesario
tener en cuenta el número de vértices que se han estirado. Si sólo se estira un vértice el resultado
será una polilínea muy diferente a la que resultaría si se desplazan dos vértices. En cualquier caso,
una vez decididos los ajustes necesarios para el contorno, las baldosas deben borrarse y las nuevas
posiciones deben volver a calcularse.
z Si se desplaza el contorno de la polilínea, todas las baldosas deberán borrarse y, a continuación,
volverse a dibujar en la nueva ubicación. Esta operación es bastante sencilla ya que el contorno de la
polilínea no ha cambiado de tamaño ni forma.
z Si los usuarios amplían o reducen el contorno de la polilínea, es necesario tomar una decisión.
¿Deben ampliarse o reducirse también las baldosas, de manera que el camino contenga el mismo
número de baldosas que antes? ¿O bien debe mantenerse el tamaño de la baldosa y hacer que la
aplicación añada o suprima baldosas, según se amplíe o reduzca la polilínea?
z Si los usuarios hacen girar el contorno de la polilínea, todas las baldosas deberían borrarse y, a
continuación, volverse a dibujar con la nueva orientación.
z Advierta al usuario antes de iniciar un comando, que el comando de edición que ha seleccionado
(como estirar, desplazar o girar) tendrá efectos negativos en el camino de jardín.
z Si el usuario continúa, borre las baldosas y no las vuelva a dibujar.
z Suprima los reactivos del contorno del camino.
Nota Además de los comandos de AutoCAD invocados por el usuario, las entidades también pueden
modificarse o suprimirse a través de aplicaciones AutoLISP u ObjectARX®. El ejemplo que se
proporciona en el Aprendizaje del Camino de jardín no trata ninguna manipulación programada sobre el
contorno de la polilínea del camino de jardín, como a través (entdel < polyline entity>). En este caso, los
eventos de reactivos de :vlr-commandWillStart y :vlr-commandEnded no se ejecutarán.
¿Comentarios?
Otro aspecto importante de la aplicación que es necesario tener en cuenta es el tipo de información que
se va a enlazar al reactivo de objeto que se crea para cada entidad de polilínea. En la lección 6, añadió
un código que enlazó el contenido de gp_PathData (la lista de asociaciones) con el reactivo. Al añadir
un nuevo campo con clave (100) a la lista de asociaciones, ha ampliado los datos de gp_PathData. Esta
nueva sublista es una lista con los punteros de todas las entidades circulares asignadas al contorno de
la polilínea.
Debido al trabajo que se necesita realizar para volver a calcular el contorno de la polilínea, es necesario
añadir a gp_pathData cuatro valores clave adicionales:
;;; StartingPoint ;
;;; (12 . BottomStartingPoint) 15------------------------14 ;
Estos puntos ordenados son necesarios para volver a calcular el contorno de las polilíneas siempre que
el usuario arrastre el pinzamiento de una esquina a una nueva ubicación. Esta información ya existe
dentro de la función gp:drawOutline del archivo gpdraw.lsp. Pero compruebe el valor devuelto de la
función. En la actualidad, sólo se devuelve el puntero al objeto de polilínea. Por lo tanto, es necesario
realizar tres cosas:
La unión de las listas de puntos del perímetro es sencillo. Mire el código de gp:drawOutline. La variable
local p1 corresponde al valor clave 12, p2 corresponde al 13, p3 al 14 y p4 al 15. Para unir esta
información, puede añadir la siguiente llamada de función:
(setq polyPoints(list
(cons 12 p1)
(cons 13 p2)
(cons 14 p3)
(cons 15 p4)
))
También es fácil modificar la función para que devuelva los puntos del perímetro de la polilínea y el
puntero a la polilínea. Como indica la última expresión dentro de gp:drawOutline, una en una lista los
dos elementos de información que desee devolver.
Para añadir al programa la lógica para guardar los puntos del perímetro de la polilínea
1. Modifique gp:drawOutline; para ello, realice los cambios que se muestran en negrita en el código
siguiente (no olvide añadir la variable local polyPoints a la secuencia defun):
(defun gp:drawOutline (BoundaryData / PathAngle
Width HalfWidth StartPt PathLength
angm90 angp90 p1 p2
p3 p4 poly2Dpoints
poly3Dpoints plineStyle pline
polyPoints
)
;; extract the values from the list BoundaryData.
(setq PathAngle (cdr (assoc 50 BoundaryData))
Width (cdr (assoc 40 BoundaryData))
HalfWidth (/ Width 2.00)
StartPt (cdr (assoc 10 BoundaryData))
PathLength (cdr (assoc 41 BoundaryData))
angp90 (+ PathAngle (Degrees->Radians 90))
angm90 (- PathAngle (Degrees->Radians 90))
p1 (polar StartPt angm90 HalfWidth)
p2 (polar p1 PathAngle PathLength)
p3 (polar p2 angp90 Width)
p4 (polar p3 (+ PathAngle
(Degrees->Radians 180)) PathLength)
poly2Dpoints (apply 'append
(mapcar '3dPoint->2dPoint (list p1 p2 p3 p4))
)
poly3Dpoints (mapcar 'float (append p1 p2 p3 p4))
;; get the polyline style.
plineStyle (strcase (cdr (assoc 4 BoundaryData)))
;; Add polyline to the model space using ActiveX automation.
pline (if (= plineStyle "LIGHT")
;; create a lightweight polyline.
(vla-addLightweightPolyline
*ModelSpace* ; Global Definition for Model Space
(gp:list->variantArray poly2Dpoints)
;data conversion
) ;_ end of vla-addLightweightPolyline
;; or create a regular polyline.
(vla-addPolyline
*ModelSpace*
(gp:list->variantArray poly3Dpoints)
;data conversion
) ;_ end of vla-addPolyline
) ;_ end of if
polyPoints (list
(cons 12 p1)
(cons 13 p2)
(cons 14 p3)
(cons 15 p4)
)
) ;_ end of setq
(vla-put-closed pline T)
(list pline polyPoints)
) ;_ end of defun
2. Modifique la función C:GPath de gpmain.lsp. Busque la línea de código que, en este momento,
es:
(setq PolylineName (gp:drawOutline gp_PathData))
¿Comentarios?
z Actualización de gp:Calculate-and-Draw-Tiles
¿Comentarios?
En la lección 6, registró dos funciones de retorno de llamada con eventos La función gp:outline-erased
se asoció al evento de reactivos :vlr-erased y gp:outline-changed se asoció al evento :vlr‑modified Es
necesario hacer que estas funciones realicen aquello para lo que fueron creadas.
Para que las funciones de retorno de llamada de reactivos de objetos realicen aquello para lo
que fueron creadas
1. En gpreact.lsp, cambie gp:outline-erased de manera que aparezca como se muestra a
continuación
(defun gp:outline-erased (outlinePoly reactor parameterList)
(setq *reactorsToRemove*
(cons reactor *reactorsToRemove*))
(princ)
) ;_ end of defun
Existen dos categorías de funciones que pueden modificar el contorno de las polilíneas. La
primera categoría contiene aquellos comandos que rompen la asociación del camino con
sus baldosas. Ya ha comprobado esta condición en gp:command‑will‑start y, en
consecuencia, ha establecido la variable global *lostAssociativity*. En este caso, es
necesario borrar las baldosas y el camino queda en manos del usuario. La otra categoría
es el modo de pinzamiento del comando ESTIRA, en el que la asociación se mantiene y es
necesario enderezar el contorno después de que el usuario haya acabado de arrastrar un
vértice a una nueva ubicación.
La variable *polyToChange* almacena un puntero de objetos de VLA a la propia polilínea.
Esto se utilizará en la función gp:command-ended cuando llegue la hora de calcular de
nuevo el marco de la polilínea.
¿Comentarios?
El pseudocódigo es relativamente sencillo, pero hay varios detalles importantes en él que, por el
momento, no es necesario que conozca..
¿Comentarios?
El primer detalle es el hecho de que la aplicación puede dibujar dos clases de polilíneas: de estilo
antiguo y ligeras. Estos tipos diferentes de polilíneas devuelven sus datos de entidad en distintos
formatos. La polilínea de estilo antiguo devuelve una lista de doce reales: cuatro juegos de puntos X, Y
y Z. Sin embargo, la polilínea ligera devuelve una lista de ocho reales: cuantro juegos de puntos X e Y.
Es necesario realizar algunos cálculos para determinar el contorno de la polilínea revisado después de
que algún usuario haya desplazado uno de los vértices. Sería mucho más fácil realizar los cálculos si
los datos de la polilínea tienen un formato consistente.
El Ejercicio 7 del archivo utils.lsp contiene funciones que realizan las conversiones de formato
necesarias: xyzList->ListOfPoints extrae y cambia el formato de las listas de puntos 3D por listas de
listas, mientras que xyList->ListOfPoints hace lo mismo con las listas de puntos 2D.
Para añadir el código para convertir los datos de la polilínea a un formato consistente, siga los
siguientes pasos
1. Si tiene una copia de utils.lsp abierta en una ventana de edición de texto VLISP, ciérrela.
2. Copie la versión de utils.lsp del directorio Tutorial\VisualLISP\Lesson7 en su directorio de trabajo.
Además de las dos funciones que vuelven a dar formato a los datos de la polilínea, utils.lsp
contiene otras funciones de utilidad necesarias para identificar las modificaciones que los
¿Comentarios?
El segundo detalle que aparece en el pseudocódigo se encuentra casi al final, en el paso en el que se
vuelven a dibujar las baldosas. Esta es la secuencia del pseudocódigo:
The parenthetical phrase says it all: force ActiveX drawing. ¿Por qué es esto necesario? ¿Por qué la
aplicación no puede utilizar la preferencia de creación de objetos que se encuentra almacenada en la
sublista de asociaciones?
La respuesta es que no se puede utilizar la función command para la creación de entidades dentro de
una función de retorno de llamada de un reactivo. Porque está relacionada con algunos procesos
internos de AutoCAD. Es necesario hacer que la rutina de dibujar baldosas utilice ActiveX. Este tema se
volverá a tratar con más detenimiento más adelante en esta misma lección.
¿Comentarios?
De esta forma se demuestra que no es posible tener la certeza de que en todos los casos se llamen a
los retornos de llamadas de reactivos de objetos.
Esta secuencia tiene asociada una peculiaridad. Incluso durante la llamada de retorno command ended
final, los círculos que aún forman parte del conjunto de selección de pinzamiento no se pueden borrar.
Estos círculos aún se encuentran abiertos en AutoCAD. Si intenta borrarlos durante la llamada de
retorno command-ended, puede provocar un fallo de AutoCAD. Para que esto no ocurra, puede utilizar
otra variable global para almacenar una lista de punteros a los objetos de baldosas hasta que puedan
borrarse.
Esta nueva función se utilizará en la primera fase de borrado de las baldosas. Observe que
las baldosas no se han borrado realmente: se han hecho invisibes y se han añadido a una
variable global denominada *Safe-to-Delete*.
2. Añada al archivo gpreact.lsp la siguiente función:
(defun Gp:Safe-Delete (activeCommand)
(if (not (equal
(strcase (substr activeCommand 1 5))
"GRIP_"
)
)
(progn
(if *Safe-to-Delete*
(foreach Item *Safe-to-Delete*
(if (not (vlax-erased-p Item))
(vla-erase item)
)
)
)
(setq *Safe-to-Delete* nil)
)
)
¿Comentarios?
Ahora, que ya ha visto el pseudocódigo y algunos de los detalles importantes, sustituya el código vacío
del retorno de llamada de reactivo gp:command-ended por lo siguiente:
(car *reactorsToChange*)
)
)
)
)
;; First, erase the tiles within the polyline border.
(gp:erase-tiles reactorToChange)
;; Next, get the current coordinate values of the polyline
;; vertices.
(setq coordinateValues
(vlax-safearray->list
(vlax-variant-value
(vla-get-coordinates *polyToChange*)
)
)
)
;; If the outline is a lightweight polyline, you have
;; 2d points, so use utility function xyList->ListOfPoints
;; to convert the coordinate data into lists of
;; ((x y) (x y) ...) points. Otherwise, use the
;; xyzList->ListOfPoints function that deals
;; with 3d points, and converts the coordinate data into
;; lists of ((x y z) (x y z) ... ) points.
(setq CurrentPoints
(if (= (vla-get-ObjectName *polytochange*) "AcDbPolyline")
(xyList->ListOfPoints coordinateValues)
(xyzList->ListOfPoints coordinateValues)
)
)
;; Send this new information to RedefinePolyBorder -- this
;; will return the new Polyline Border
(setq NewReactorData
(gp:RedefinePolyBorder CurrentPoints reactorData)
)
;; Get all the border Points and ...
(setq newpts (list (cdr (assoc 12 NewReactorData))
(cdr (assoc 13 NewReactorData))
(cdr (assoc 14 NewReactorData))
(cdr (assoc 15 NewReactorData))
)
)
;; ...update the outline of the polyline with the new points
;; calculated above. If dealing with a lightweight polyline,
;; convert these points to 2D (since all the points in
;; newpts are 3D), otherwise leave them alone.
(if (= (cdr (assoc 4 NewReactorData)) "LIGHT")
(setq newpts (mapcar '(lambda (point)
(3dPoint->2dPoint Point)
)
newpts
)
)
)
;; Now update the polyline with the correct points.
(vla-put-coordinates
*polytochange*
;; For description of the list->variantArray see utils.lsp.
(gp:list->variantArray (apply 'append newpts))
)
;; Now use the current definition of the NewReactorData,
¿Comentarios?
NewReactorData
;; Object creation function.
;; Within a reactor this *MUST* be ActiveX.
"ActiveX"
)
)
Observe que se especifica un solo parámetro y que ObjectCreationStyle se define como una variable
local. Revise cómo se establece la variable ObjectCreationStyle, que se encuentra un poco más
adelante en la función:
Observe que si declara una variable como un parámetro (antes de la barra oblicua) y como
una variable local (después de la barra oblicua), VLISP se lo señalará. Por ejemplo, si
declara ObjectCreationStyle como un parámetro y también como una variable y, a
continuación, utiliza la herramienta de comprobación de sintaxis de VLISP en la función
gp:Calculate‑and‑Draw‑Tiles, en la ventana Generar salida aparecerá el siguiente
mensaje:
; *** WARNING: same symbol before and after / in arguments list: OBJECTCREA
¿Comentarios?
Recuerde que en la lección 4 se señalaba que siempre que cambie una función vacía, es necesario
hacerse la siguiente pregunta:
z ¿Ha cambiado la llamada a la función? Es decir, ¿aún toma la función el mismo número de
argumentos?
z ¿Devuelve la función algo diferente?
Estas mismas preguntas hay que hacerlas siempre que se realice un cambio significativo en una
función de trabajo mientras se integran, afinan y actualizan las aplicaciones. En este caso, es necesario
encontrar cualquier otra función en el proyecto que llame a gp:Calculate-and-Draw-Tiles. VLISP tiene
una función para ayudarle en esta tarea.
No obstante, aún queda mucho trabajo por realizar y todo tiene que ver con este fragmento de código
de la función gp:Command-ended:
(setq NewReactorData
(gp:RedefinePolyBorder CurrentPoints reactorData)
) ;_ end of setq
¿Comentarios?
Ha realizado un gran trabajo hasta llegar a este punto y probablemente haya aprendido muchos
conceptos, términos, comandos y órdenes nuevos, suficientes por el momento. Teniendo todo esto en
cuenta, se recomienda copiar el código de ejemplo que se suministra en el aprendizaje en vez de tener
que teclearlo.
¿Comentarios?
El archivo gppoly.lsp contiene un número de funciones que son necesarias para enderezar una
polilínea cuando se ha estirado un único pinzamiento. En este aprendizaje se explicarán con detalle
sólo algunas de estas funciones.
Nota Esta sección del Aprendizaje de Camino de jardín contiene algunos de los códigos y conceptos
más complejos de toda la lección. Si es un principiante, puede saltar hasta la sección Crear una
aplicación.
Las funciones del archivo gppoly.lsp se organizan del mismo modo que ha podido observar en los
archivos de código fuente de AutoLISP. La función de más alto nivel, normalmente la función principal o
C: (en este caso, gp:Redefine-PolyBorder) se encuentra al final del archivo. Las funciones llamadas
desde la función principal se definen encima de ésta dentro del archivo fuente. Esta convención nos
lleva al pasado de la programación cuando en algunos entornos era necesario organizar los archivos de
esta manera. En VLISP, es una cuestión de estilo personal ya que no es necesario organizar las
funciones siguiendo ninguna secuencia específica.
Antes de profundizar en los detalles, retroceda y vea lo que es necesario hacer para calcular de nuevo y
dibujar el contorno del camino de jardín. En la siguiente ilustración se muestra un ejemplo de camino de
jardín, junto con los puntos clave de la lista de asociaciones almacenados en los datos del reactivo:
En este ejemplo, el punto clave 12 es la esquina inferior izquierda, el 13 es la esquina inferior derecha,
etc. Si el usuario desplaza el punto superior derecho (el punto clave 14 ), el programa necesitará volver
a calcular dos puntos ya existentes, el inferior derecho (13) y el superior izquierdo (15).
¿Comentarios?
Function gp:RedefinePolyBorder
Extract the previous polyline corner points (12, 13, 14, and 15
key values).
Find the moved corner point by comparing the previous
polyline corner points with the current corner points.
(The one "misfit" point will be the point that moved.)
Set the new corner points by recalculating the two points
adjacent to the moved point.
Update the new corner points in the reactor data (that will
be stored back in the reactor for the modified polyline).
Update other information in the reactor data. (Start point,
endpoint, width, and length of path need to be recalculated.)
¿Comentarios?
La función gp:FindMovedPoint contiene varias expresiones LISP muy útilies para la manipulación de
listas. Principalmente, lo que hace esta función es comparar la lista de los puntos actuales de la
polilínea (después de que el usuario arrastrara uno a una nueva ubicación) con los puntos anteriores y
devolver la lista con claves (13 <xvalue> <yvalue>) del punto desplazado.
La mejor manera de ver cómo trabaja esta función es revisar el código y observar los valores que
manipula. Establezca un punto de interrupción justo en la primera expresión (setq result . . .) y observe
las siguientes variables mientras revisa la función:
z KeyListToLookFor
z PresentPoints
z KeyedList
z Result
z KeyListStatus
z MissingKey
z MovedPoint
Las funciones mapcar y lambda se examinarán en la siguiente sección. Por el momento, siga los
comentarios del código para ver si puede entender lo que sucede en las funciones.
¿Comentarios?
La función mapcar aplica (mapea) una expresión a cada elemento de una lista. Por ejemplo, en una
lista de enteros 1, 2, 3 y 4, puede utilizar mapcar para aplicar la función1+ y añadir 1 a cada número de
la lista:
Una primera definición de mapcar podría ser que aplica la función dada en el primer parámetro a los
sucesivos elementos del segundo parámetro, la lista. El valor que resulta de una operación de mapcar
es la lista transformada por la función o expresión que se le aplica. En realidad, mapcar puede hacer
aún más, pero esta definición será suficiente por el momento.
En el ejemplo que se proporciona, todos los valores de la lista '(1 2 3 4) han pasado por la función 1+.
Principalmente, mapcar ha realizado las siguientes operaciones y ha unido los valores obtenidos en una
lista:
(1+ 1) -> 2
(1+ 2) -> 3
(1+ 3) -> 4
(1+ 4) -> 5
Este es otro ejemplo de mapcar, esta vez se utiliza la función null para probar si los valores de una lista
son o no valores nulos (no verdaderos):
Puede utilizar muchas funciones de AutoLISP ya existentes dentro de un mapcar. También puede
utilizar sus propias funciones. Por ejemplo, imagine que acaba de crear una función muy potente
denominada equals2:
Parece que equals2 no es tan potente. Es en casos como este en los que lambda nos viene bien.
Puede utilizar lambda en casos en los que no desea o no necesita definir una función. En ocasiones
encontrará lambda definida como una función anónima. Por ejemplo, en vez de definir una función
denominada equals2, podría escribir una expresión lambda para realizar la misma operación sin la
sobrecarga de definir la función:
(= 1 2) -> nil
(= 2 2) -> T
(= 3 2) -> nil
(= 4 2) -> nil
Con toda esta información, compruebe si la función gp:FindPointInList tiene sentido. Revise de nuevo
los comentarios del código fuente.
¿Comentarios?
La clave para entender cómo funciona gp:recalcPolyCorners está en volver a consultar el diagrama que
muestra lo que representan los valores del 12 al 15:
En el diagrama, el usuario ha desplazado el punto de la esquina asociado al valor clave 14. Lo que
supone volver a calcular los puntos de esquina asociados al 13 y al 15.
Es necesario desplazar el punto 15 a lo largo del vector definido por el punto 12 al punto 15 hasta que
se alinee con el nuevo punto 14. Los vectores del 12 al 15 y del 14 al 15 deben ser perpendiculares los
unos con los otros. La misma operación se debe aplicar para volver a calcular la nueva ubicación del
punto 13.
¿Comentarios?
Estas tres funciones son necesarias para estudiar al fin una de las peculiaridades de la programación
en un sistema AutoCAD que, como sabe, permite obtener datos de gran precisión. No obstante, en
ocasiones los números no son todo lo precisos que debieran por el redondeo hacia arriba o hacia abajo
de los valores de coma flotante que definen posiciones geométricas. Debe saber comparar un conjunto
de puntos con otros ya que deberá tratar estos casos.
Ha observado en alguna ocasión que algunas veces cuando enumera la información asociada a una
entidad de AutoCAD, ve un valor como 1.0e-017? Este número es casi cero, aunque cuando lo
compara con cero en un programa LISP, casi no se tiene cuenta.
En el camino de jardín, debe saber comparar los números sin necesidad de preocuparse por el hecho
de que 1.0e-017 no sea igual a cero. Las funciones gp:pointEqual, gp:rtos2 y gp:zeroSmallNum
permiten identificar cualquier discrepancia en el redondeo cuando compara listas de puntos.
¿Comentarios?
z Ha modificado la función gp:drawOutline de manera que devuelva los puntos del perímetro de la
polilínea además del puntero a la polilínea. Ha añadido esta información a la variable gp_PathData.
Esta variable se almacena con los datos del reactivo en el reactivo de objetos adjunto a cada camino
de jardín.
z Ha actualizado las funciones de reactivos en gpreact.lsp.
z Ha añadido funciones xyzList->ListOfPoints, xyList‑>ListOfPointsy otras funciones de utilidades al
archivo utils.lsp .
z Ha actualizado la función gp:Calculate-and-Draw-Tiles de manera que ObjectCreationStyle es ahora
un parámetro para la función y no una variable local.
z Ha modificado la llamada a gp:Calculate-and-Draw-Tiles en la función C:GPath dentro del archivo
gpmain.lsp.
z Ha añadido gppoly.lsp a su proyecto y examinado las funciones que en él se encuentran.
Conceda una oportunidad a la aplicación finalizada. Guarde su trabajo y, a continuación, cargue las
fuentes del proyecto; ejecute la función Gpath e intente estirar y desplazar el contorno del camino del
jardín. Recuerde: si algo no funciona y no consigue resolver el problema satisfactoriamente, puede
cargar el código completo desde el directorio Tutorial\VisualLISP\Lesson7.
¿Comentarios?
La última tarea dentro de este aprendizaje es coger su código del camino de jardín y convertirlo en una
aplicación independiente. De esta manera, se puede distribuir como un único ejecutable para cualquier
usuario o cliente. Afortunadamente, este último conjunto de tareas es probablemente el más fácil de
todo el aprendizaje ya que VLISP hace prácticamente todo el trabajo.
Nota Se recomienda que proceda a integrar una aplicación sólo en el caso de que su código esté
funcionando perfectamente. Asegúrese que ha probado la aplicación con los archivos fuente originales
y que está conforme con los resultados.
¿Comentarios?
Para ayudarle a crear aplicaciones independientes, VLISP proporciona el asistente Crear aplicación.
Cuando todo haya acabado, tendrá un archivo ejecutable denominado gardenpath.vlx. Para probarlo,
realice lo siguiente:
¿Comentarios?
¡Ya ha llegado al final del camino! Como ha podido comprobar, en este aprendizaje se han cubierto
muchos temas. Se han introducido conceptos de AutoLISP así como operaciones de VLISP. El "Repaso
del camino de jardín" se ha diseñado como ejemplo para muchos de los temas y conceptos. Quizá esté
interesado en obtener más información. A continuación encontrará una breve bibliografía de algunos de
los libros LISP y AutoLISP.
¿Comentarios?
Understanding AutoLISP: Programming for Productivity, William Kramer, Autodesk Press, ISBN 0-8273-
5832-6.
AutoLISP in Plain English: A Practical Guide for Non-Programmers, George O. Head, Ventana Press,
ISBN: 1566041406.
LISP, 3rd Edition, Patrick Henry Winston and Berthold Klaus Paul Horn, Addison-Wesley Publishing
Company, ISBN 0-201-08319-1.
Common LISP, The Language, Second Edition, Guy L. Steele, Jr., Digital Press, ISBN 1-55558-041-6.
¿Comentarios?