tutorial AutoLISP

CAPÍTULO I Primeros pasos con AutoLISP Objetivos del curso Aprovechando que actualmente dispongo de algo más de tiempo,

Views 194 Downloads 3 File size 964KB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

CAPÍTULO I Primeros pasos con AutoLISP

Objetivos del curso Aprovechando que actualmente dispongo de algo más de tiempo, me he decidido a desempolvar y re-editar algún curso que he creado hace ya bastante tiempo, trataré de actualizar el contenido para que funcione con las últimas versiones de AutoCAD. Con este curso no se pretende formar a expertos programadores. Más bien, se pretende acercar el lenguaje de programación AutoLISP a los usuarios de AutoCAD, para que puedan crear utilidades y pequeñas aplicaciones que les ahorren tiempo y cálculos en sus trabajos. Para seguir el curso no son necesarios conocimientos de programación, tan sólo es preciso tener unas nociones básicas de AutoCAD y del entorno Windows. Otro de los objetivos del curso es el de ver a AutoCAD no como un programa de dibujo, sino como una potente base de datos gráficos. Y descubrir cómo funciona esa base de datos.

Código de una macro en AutoLISP

Merece la pena aprender a programar sobre AutoCAD Desde luego que puede merecer la pena. Puede agilizar y facilitar enormemente el trabajo con AutoCAD. Puede que existan en el mercado aplicaciones de diseño mucho más potentes para trabajar en 3D de forma paramétrica, como SolidWorks o Catia, pero AutoCAD se sigue utilizando habitualmente en infinidad de aplicaciones. Para trabajar en 2D sigue siendo la aplicación de CAD de referencia y su formato de archivo .DWG es un estándar en la importación y exportación de archivos convirtiéndose en uno de los formatos de archivo más utilizados para intercambiar partes de un diseño con clientes, proveedores y colaboradores. AutoLISP es un lenguaje sencillo de aprender y a diferencia de otros lenguajes de programación se pueden crear programas interesantes y realmente útiles casi desde el primer momento. Muchas veces, la mayoría del tiempo empleado en crear una macro o programa se emplea en depurar el código (corregir errores). La planificación y el análisis previo de la macro son de suma importancia para evitarlo. El primer paso para escribir una macro consiste en escribir en un papel con un lenguaje simple lo que se desea que realice el programa, a esto se le denomina pseudocódigo. Un método algo más avanzado y eficaz es utilizar diagramas de flujo. Cuando ya empieces a programar en AutoLISP por ti solo, te será muy útil reunir todas las anotaciones sobre proyectos de aplicaciones y macros en una libreta o bloc de notas. También puedes utilizar Evernote o cualquier otro sistema que te permita almacenar de información o gestionar las macros que has creado.

Interfaces de programación AutoCAD dispone varios entornos de programación, la selección del tipo de interfaz a emplear para crear una aplicación dependerá de las necesidades de la aplicación y de la experiencia o conocimientos del programador/es: 

AutoLISP es una adaptación del lenguaje de programación CommonLISP para AutoCAD. Es sencillo de aprender y al mismo tiempo potente. AutoCAD cuenta con un intérprete interno de LISP que permite introducir código desde la línea de comandos o cargar programas desde archivos externos. Puedes utilizar AutoLISP para automatizar tareas repetitivas y crear nuevos comandos de AutoCAD



ActiveX Automation constituye una alternativa moderna al AutoLISP. Puedes acceder y controlar objetos de AutoCAD desde cualquier aplicación que utilice un controlador Automation como Visual Basic y Delphi, o cualquiera de las aplicaciones que dispongan de Visual Basic for Applications (VBA)



VBA es un entorno de programación basado en objetos que utiliza íntegramente la sintaxis del lenguaje Visual Basic y permite usar controles ActiveX. Permite también la integración con otras aplicaciones que utilizan VBA como MS Office o MS Project. Las ediciones de desarrollo de MS Visual Basic, que se adquieren por separado, complementan AutoCAD VBA con componentes adicionales como un dispositivo externo de base de datos y funcionalidades de escritura de informes



ObjectARX es un entorno de programación de lenguaje compilado para el desarrollo de aplicaciones de AutoCAD. El entorno de programación ObjectARX incluye varias bibliotecas de vínculos dinámicos (DLL) que ofrecen acceso directo a las estructuras de la base de datos, el sistema de gráficos y los dispositivos de geometría de AutoCAD

De momento nos centraremos en la programación sobre AutoCAD utilizando el intérprete interno de AutoLISP para pasar en breve al entorno de Visual LISP y posteriormente a la programación en VBA.

Características de AutoLISP El LISP fue desarrollado a finales de los años 50 por John McCarthy, es el segundo lenguaje de programación más antiguo solo precedido por Fortran. Uno de los campos en los que es más empleado es en la investigación en inteligencia artificial. Su nombre proviene de LISt Procesing (Procesado de listas), pues se basa en el uso de listas en lugar de datos numéricos. Aunque hay quien mantiene en tono jocoso que su nombre proviene de “Lost In Stupid Parenthesis”. AutoLISP añade al LISP algunas funciones especialmente diseñadas para la manipulación de las entidades de AutoCAD. AutoLISP es un lenguaje evaluado. El código se convierte a lenguaje máquina (ceros y unos) y se almacena en la memoria temporal. No es tan lento como los lenguajes interpretados, ni tan rápido como los compilados: 

en los lenguajes interpretados, se va traduciendo el programa a código máquina (el idioma de los ordenadores) a medida que se ejecuta



en los lenguajes compilados, el código fuente (texto) del programa se traduce a código máquina generando un archivo ejecutable (.EXE) que es el que ejecutará el programa

Hay quien dice que AutoLISP es lento comparándolo con otros lenguajes como Pascal o C, especialmente cuando se utilizan muchas funciones matemáticas. Sin embargo, cuando se utiliza manipulando símbolos los resultados son muy distintos. Esto hace de AutoLISP un excelente lenguaje para la programación de sistemas CAD, ya que un sistema CAD no es más que un entorno de manipulación de símbolos gráficos. Una de las características más importantes de AutoLISP es la posibilidad de acceder a la base de datos de los dibujos de AutoCAD. Donde podemos acceder a las capas, estilos de texto, sistemas de coordenadas personales (SCP)… así como a todas las entidades del dibujo. Esta información se puede modificar, extraer e incluso añadir más entidades al dibujo o información adicional. La mayoría de los primeros lenguajes de programación de alto nivel como C o Pascal son lenguajes procedimentales, se ejecutan una serie de instrucciones paso a paso a partir de unos datos. En AutoLISP en lugar de seguir instrucciones prefijadas, se puede acceder a la información de los objetos de AutoCAD para determinar de qué información se dispone, preguntándole a los objetos por ellos mismos. AutoLISP no es estrictamente un lenguaje orientado al objeto, pero contiene bastantes características de estos tipos de lenguajes debido a que puede interactuar con los objetos contenidos en los dibujos.

AutoLISP permite crear nuevos comandos para AutoCAD, que se ejecuten como cualquier otra orden. Es posible incluso redefinir los comandos de AutoCAD para que funcionen de forma distinta, por ejemplo podríamos redefinir el comando “POLIGONO” para que dibuje polígonos estrellados en lugar de los regulares. Una mención especial merece el uso de AutoLISP en la realización de programas paramétricos y en el diseño semiautomático, ya que entre el 60% y el 80% del diseño está dedicado a la modificación de diseños previos. En los programas paramétricos, el usuario introduce una serie de datos o parámetros a partir de los cuales el programa realiza el diseño completo de un elemento u objeto. Esta puede ser sin ninguna duda una de las mayores aplicaciones de AutoLISP. AutoLISP se ha mejorado con la creación de Visual LISP que ofrece un entorno de desarrollo integrado dentro de AutoCAD. Visual LISP incluye un compilador, un depurador y diversas utilidades para facilitar la programación. Además añade nuevos comandos para permitir la interacción con objetos que utilizan ActiveX. Otra de las novedades que aporta Visual LISP son los reactores de objetos que permiten que AutoLISP responda a eventos. En los dos primeros capítulos del curso se trabajará desde la ventana de comandos de AutoCAD. A partir del tercer capítulo ya crearemos nuestras macros en archivos de texto utilizando el entorno de Visual LISP proporcionado por AutoCAD. Existen dos inconvenientes al emplear AutoLISP desde la ventana de comandos de AutoCAD: 

en primer lugar el reducido espacio con el que se cuenta en la ventana de comandos de AutoCAD y la dificultad que supone el no tabular el código, es decir escribir el código todo seguido. Esto es debido a que cada vez que se pulsa Intro, AutoCAD evalúa lo que se ha escrito



en segundo lugar, al terminar la sesión de trabajo en AutoCAD se perderán todos los datos almacenados en la memoria temporal

Expresiones y procedimientos de evaluación Un programa en AutoLISP consiste en una serie de expresiones del tipo “(función argumentos)“. Cada expresión comienza con un paréntesis de apertura al que sigue el nombre de una función de AutoLISP (o una función creada por el usuario) y una serie de argumentos (a veces opcionales) que dependen de la función indicada y van separados por al menos un espacio en blanco. Cada expresión termina con un paréntesis de cierre, esto es muy importante pues el número de paréntesis de apertura debe ser igual al de cierre. Cada expresión de AutoLISP devuelve un valor. Un argumento también puede ser una expresión, creándose así una estructura formada por expresiones (listas) anidadas unas dentro de otras; de modo que la más interior devolverá su resultado como un argumento a la lista exterior. Cuando existen listas anidadas (unas dentro de otras), primero se evalúan las más interiores.

Los primeros ejemplos que vamos a ver son sencillos y cortitos, así que puedes teclearlos directamente en la ventana de comandos de AutoCAD.

Ejemplo: (+ 1 2) Ejecuta la función + que realiza la suma de los argumentos 1 y 2 devuelve el resultado 3. (+ 31 22 -3) Ejecuta la función + que realiza la suma de los argumentos 31, 22 y -3 devuelve el resultado 50. Prueba también: (- 17 2) (+ 2.5 22.8) (- 0.25 22.5) (+ 12 -2 31 -7.5) Ejemplo: (+ (* 2 3) 2) devuelve 8. Recuerda que primero evalúa la lista interior y devuelve su resultado a la exterior. (+ 7 (/ 5.0 2) -3) devuelve 6.5. ¿Qué sucedería si al escribir la expresión (+ 1 (/ 5.0 2)) nos olvidásemos de escribir el último paréntesis? Haz la prueba, veras que AutoCAD te indica que falta 1 paréntesis de cierre. Si el intérprete de comandos de AutoCAD encuentra un paréntesis de apertura, supone que todo lo que vaya a continuación hasta el paréntesis de cierre es una expresión de AutoLISP. De modo que envía esa expresión al intérprete de AutoLISP para que la evalúe. En el siguiente capítulo veremos algunas de las operaciones matemáticas que se pueden realizar con AutoLISP.

Tipos de objetos y datos Los elementos de las expresiones de AutoLISP pueden ser símbolos, valores concretos y también otras expresiones. Se pueden distinguir los siguientes tipos de elementos: 

Símbolos: Cualquier elemento que no sea un valor concreto. Por ejemplo una variable, una función



Enteros: Números enteros comprendidos entre -32000 y 32000



Reales: Números reales (180 es un número entero, pero 180.0 es un número real)



Cadenas de texto: Texto con una longitud máxima de 132 caracteres



Descriptores de archivo: Representan un archivo de texto ASCII abierto



Nombres de entidades de AutoCAD: Nombre en hexadecimal de una entidad del dibujo



Conjuntos designados por el usuario: Conjuntos de entidades de AutoCAD



Funciones de usuario: Funciones definidas por el usuario



Funciones de AutoLISP: Funciones o comandos predefinidos de AutoLISP

Ejemplo: (+ 5 2) devuelve 7. (+ 5 2.0) devuelve 7.0. En el primer caso todos los argumentos son números enteros, y el resultado de su suma es un número entero. En el segundo caso, tenemos un número real, así que el resultado es un número real. Para que comprendas la importancia de esta distinción, realiza el siguiente ejemplo: (/ 5 2) (/ 5 2.0) Una de las mayores virtudes de AutoLISP es que pueden ejecutarse expresiones en medio de un comando de AutoCAD. Ejemplo: Ejecuta el comando “LINEA” e indica el primer punto, activa el forzado ortogonal (F8) y teclea… (+ 11 25) Esto devuelve el resultado de la suma al comando que se estaba ejecutando. Por eso dibuja una línea de 36 mm de longitud en la dirección que indicaba el cursor. Prueba ahora indicando el radio de la circunferencia (- 70 25) al utilizar el comando llamado “CIRCULO” (mal llamado, pues debería ser “circunferencia”). Podemos emplear también la constante PI = 3.14159… para realizar cálculos. Por ejemplo, ejecuta de nuevo el comando “CIRCULO” y cuando pregunte el radio de la circunferencia, teclea (/ 6 (* 2 PI)). Obtendremos una circunferencia de perímetro 6.

Notación empleada Para describir las funciones de AutoLISP que se expliquen a lo largo del curso se seguirá la siguiente notación: (FUNCIÓN Argumentos_necesarios [Argumentos_opcionales] ) Los nombres de las funciones de AutoLISP y el código a teclear se mostrarán en negrita.

Convenciones recomendadas

En este apartado se indicarán una serie de convenciones recomendables a la hora de programar. Alguna de ellas puede que aún no las entiendas, pero no te preocupes porque las iremos recordando a medida que avancemos en el curso. para los comentarios incluidos en el código, se recomienda utilizar el siguiente método:

 

;;; Antes del código de las funciones, explicando su funcionamiento



;; En medio del código del programa



; Para explicar una línea de código. A diferencia de las anteriores, esta no se inserta en la columna 1, se insertará al terminar el código de la línea que comenta



es muy recomendable utilizar un formato tabulado para el código



evitar el uso de variables globales innecesarias



utilizar los comandos de AutoCAD y sus opciones en Inglés y precedidos por “._”



no abusar de la función “SETQ”



no utilizar T, MIN, MAX, LAST como símbolos (nombres de variables y funciones)



recuperar el valor inicial de las variables de sistema de AutoCAD que han sido modificadas



añadir unas líneas al final del programa para indicar el nombre del nuevo comando, autor, etc.



no introducir demasiado código en la función principal



incluir una función de tratamiento de errores



evitar que el usuario pueda introducir datos erróneos



en general es recomendable que, tras ejecutar el nuevo comando, si se ejecuta el comando “DESHACER” (“H”) se deshagan todos los cambios realizados por el comando

  Convenciones utilizadas en el curso El código en AutoLISP se mostrará en negrita y con los siguientes colores:

 

los paréntesis de apertura y cierre se mostrarán en rojo ( )



las funciones de AutoLISP se mostrarán en azul setq



los números se mostrarán en verde 125.6 12



los textos se mostrarán en rosa “/nDesigne un punto: “



los comentarios se mostrarán en púrpura ;Esto es un comentario



las funciones de usuario se mostrarán en mayúsculas (GAR 90.0) 

CAPÍTULO II Las variables en AutoLISP

¿Qué es una variable? ¿No sabes qué es una variable? Recuerdas cuando en la escuela te decían “Tengo 3 melones, le doy uno a Juan y después de comprar otros 2, me comí uno porque tenía hambre”. Pues los melones son una variable. Nosotros hacíamos: 1º tengo 3 melones x=3 (x es nuestra variable). Luego le doy uno a Juan x = 3-1=2. Compro otros dos x = 2+2=4 y me comí uno x=4-1. Así que x=3. “x” no es más que un valor que varía (o puede hacerlo) a lo largo del tiempo. Pero podíamos haber llamado a la variable “y”o “z”, y ¿por qué no “melones”?

(SETQ Variable1 Valor1 [Variable2 Valor2 ... ] ) En el artículo anterior logramos dibujar una circunferencia de 6 mm de indicando el radio de la circunferencia a través de la expresión en AutoLISP (/ 6 (* 2 PI)). Pero que sucede si queremos utilizar el valor obtenido por (/ 6 (* 2PI)) para realizar otra operación, tendremos que volver a teclear la expresión anterior. ¿Existe alguna opción para almacenar en memoria los resultados de una operación, tal como hace una calculadora? Desde luego que sí, AutoLISP permite almacenar miles de datos en variables, que se almacenan en la memoria del ordenador. La función de AutoLISP empleada para definir variables y asignarles un valor es SETQ y permite definir varias variables en una misma expresión. La función SETQ devuelve el valor de la última variable definida. Con respecto a los nombres utilizados para designar a los símbolos creados por el usuario (variables y funciones): 

no son sensibles a las mayúsculas. Es lo mismo bloque, BLOQUE y Bloque



no pueden contener espacios en blanco, ni los caracteres ” ( ) . ; ‘



pueden contener números, pero no estar formados únicamente por ellos



no utilizaremos nombres de variables que coincidan con los nombres de las funciones de AutoLISP ni de las variables de sistema de AutoCAD



no existe límite en cuanto al número de caracteres, pero es recomendable emplear nombres cortos y descriptivos.

no es necesario declarar previamente las variables, como sucede en otros lenguajes de



programación. En AutoLISP una misma variable puede contener primero un número entero, luego uno real, después una cadena de texto, etc. Ejemplo: (setq a 3) Esta expresión crea la variable a y le asigna el valor 3. Devuelve el valor de la variable a. ¿Qué valor crees que devolverá (setq b (+ 1 3) melones 23.0)? Fíjate que se han definido dos variables b ymelones de valor 4 y 23.0 respectivamente. Como melones ha sido la última variable evaluada, la expresión anterior devuelve su valor. Si el intérprete de comandos de AutoCAD recibe una palabra precedida por el signo de exclamación “!“, comprobará si ese término ha sido empleado como un símbolo (nombre de variable o una función de usuario) de AutoLISP. En cuyo caso devolverá su valor y en caso contrario devolverá nil, que es lo mismo que nada o vacío. Por ejemplo: !a devuelve 3. Prueba también las siguientes expresiones y comprueba los valores asignados a las variables: (setq c (+ b 3)) Los valores de las variables también pueden utilizarse para realizar operaciones y asignar el resultado de dichas operaciones a una variable. (setq d b) Se puede definir una variable igual al valor de otra variable. (setq a 3.5) Se puede modificar el valor asociado a una variable, por eso se llama variable. (setq b 2.0) ¿Qué sucede con el valor de la variable d? ¿Tomará el nuevo valor asignado a la variable b o seguirá con el anterior? Al definir la variable d se le asignó el valor que tenía la variable b en ese momento, no se estableció ninguna relación entre ambas variables. También se puede asignar valores no numéricos a las variables. Por ejemplo, una cadena de texto. Las cadenas de texto son secuencias de uno o más caracteres encerrados entre comillas. Dentro de una cadena de texto pueden insertarse una serie de caracteres de control: 

\\ representa al símbolo \



\” representa al símbolo “



\n representa un cambio de línea (retorno del carro)



\t representa una tabulación

(setq a “\tBienvenido al Curso Introducción a la programación en AutoCAD” ) En este caso además, hay que notar que en la variable a primero se almacenaba un valor entero, luego uno real y ahora una cadena de texto. La posibilidad de reutilizar variables con distintos tipos de datos puede ser muy útil, pues dota al programador de una gran flexibilidad al escribir el código. Pero también supone que se requiera más cuidado para no pasar a una función una variable con un tipo de dato incorrecto. Por ejemplo: (+ a b) ya que ahora la variable a contiene una cadena de texto en lugar de un número. Existen una serie de símbolos predefinidos:



T Representa al valor lógico Cierto o Verdadero.



nil Representa al valor lógico Falso.

(setq b T) (setq c nil) En este caso, a la variable a se le ha asignado una cadena de texto, a la variable b el valor Cierto o Verdadero y a la variable c el valor Vacío o Falso. Para almacenar el radio de la circunferencia de perímetro 6 unidades en una variable podemos teclear: (setq rad (/ 6 (* 2 PI))) Podríamos crear una circunferencia e indicar !rad a AutoCAD cuando nos pregunte por el radio deseado. Ejemplo: ¿Por qué las siguientes expresiones están mal? (setq 157 25) (setq rad 5 Rad 4) (setq b Curso de AutoLISP) Por último veamos porque no debemos emplear para los nombres de las variables los nombres de las funciones de AutoLISP. (setq + 23) En este caso asignamos a la variable + el valor 23. Prueba (+ 5 2.5) Ahora + no representa a la función suma, sino a una variable de valor 23. De modo que el primer término de la expresión anterior ahora no es una función, por lo que da un error. Para recuperar el modo habitual de trabajo de la función + es necesario cerrar la sesión actual de AutoCAD e iniciar una nueva sesión.

CAPÍTULO III Operaciones matemáticas básicas

En los artículos anteriores ya vimos algún ejemplo de cómo funcionan las funciones matemáticas básicas (suma, resta, división y multiplicación). Ahora explicaremos su funcionamiento con mayor profundidad.

(+ [número1 número2 ... ] ) Suma los números indicados como argumentos y devuelve el resultado de dicha suma. Si todos los números de la lista son enteros, el resultado también será entero. (+ 3 9) devuelve 12 (+ 3.0 9) devuelve 12.0 (setq a (+ 3 9 4)) devuelve 16 y lo almacena en la variable a (+ 3.5 -1) devuelve 2.5 Prueba ahora la siguiente expresión: (setq a (+ a 2.5)) devuelve 18.5 Hemos asignado a la variable a el resultado de una operación en la que usamos el anterior valor asignado a la variable a como uno de los argumentos de la operación. Si le pasamos a la función + un único número como argumento, nos devuelve ese número. (+ 12.5) devuelve 12.5 (+ -7.0) devuelve -7.0 Si ejecutamos la función suma sin argumentos, devuelve 0. (+ ) devuelve 0

La expresión (+ .5 2) nos dará un error. Los números reales siempre deben comenzar por un número entero, incluso si es cero, seguido de la parte decimal. (+ 0.5 2) devuelve 2.5 (+ 3 -0.6) devuelve 2.4

(- [número1 número2 ... ] ) Resta al primer número todos los siguientes números pasados como argumentos. Si todos los números de la lista son enteros, el resultado también será entero. (- 11.0 5) devuelve 6.0 (- 11 5) devuelve 6 (setq a (- 12 5 4)) devuelve 3 y lo almacena en la variable a (- 3.5 -1) devuelve -4.5 (setq a (- a 2.5)) devuelve 0.5 y lo almacena en la variable a. Si le pasamos a la función - un único número como argumento, nos devuelve ese número cambiado de signo. (- 12.5) devuelve -12.5 (- -7.0) devuelve 7.0 En la expresión anterior, el primer signo “-” representa a la función resta, mientras que el segundo representa el signo de un número negativo. Si ejecutamos la función resta sin argumentos, devuelve 0. (- ) devuelve 0

(* [número1 número2 ... ] ) Multiplica los números indicados como argumentos y devuelve el resultado de dicho producto. Si todos los números de la lista son enteros, el resultado también será entero. (* 3 9) devuelve 27

(* 3.0 9) devuelve 27.0 (setq a (* 3 9 4)) devuelve 108 y lo almacena en la variable a (* 3.5 -1) devuelve -3.5 Prueba ahora la siguiente expresión: (setq a (* a 2.5)) devuelve 270.0 Si le pasamos a la función * un único número como argumento, nos devuelve ese número. (* 12.5) devuelve 12.5 (* -7.0) devuelve -7.0 Si ejecutamos la función * sin argumentos, devuelve 0. (* ) devuelve 0

(/ [número1 número2 ... ] ) Divide el primer número entre el siguiente y devuelve el resultado. Si se pasan más de dos números como argumentos, el primer número se dividirá entre el producto de los restantes números. (/ 45 5 3) devuelve 3 (/ 11 5.5) devuelve 2.0 En esta función es muy importante recordar que si todos los números de la lista son enteros, el resultado también será entero. (/ 7 2) devuelve 3 (/ 7 2.0) devuelve 3.5 (setq a (/ 12.5 4 2)) devuelve 1.5625 y lo almacena en la variable a (/ 3.5 -1) devuelve -3.5 (setq a (/ a 0.5)) devuelve 3.125 y lo almacena en la variable a Si le pasamos a la función / un único número como argumento, nos devuelve ese número. (/ 12.5) devuelve 12.5

(/ -7.0) devuelve -7.0 Si ejecutamos la función / sin argumentos, devuelve 0. (/ ) devuelve 0

(1+ ) Esta función incrementa en una unidad el número indicado como argumento. (1+ 5) devuelve 6. Ojo entre “1” y “+” no debe haber ningún espacio, ya que el nombre de la función es “1+“. (1+ 2.5) devuelve 3.5 (1+ 0) devuelve 1 (1+ -7) devuelve -6 Una aplicación bastante habitual de esta función es la de incrementar índices o contadores: (setq i 1) (setq i (1+ i)) devuelve 2

(1- ) Esta función reduce en una unidad el número indicado. (1- 5) devuelve 4. Ojo entre “1” y “-” no debe haber ningún espacio, ya que el nombre de la función es “1-“. (1- 2.5) devuelve 1.5 (1- 0) devuelve -1 (1- -1) devuelve -2

CAPITULO IV Solicitar números al usuario

En este artículo veremos dos funciones de AutoLISP que nos permitirán solicitar al usuario un número entero o real. Esto nos permitirá interactuar con el usuario y pedirle información.

(GETINT [mensaje] ) Solicita del usuario un número entero. En caso de que el usuario introduzca un número real o cualquier otro dato que no sea un número entero, AutoCAD recordará mediante un mensaje que está solicitando un número entero y no finalizará la ejecución de la función hasta que se introduzca un valor entero, o se pulse ESC para cancelar su ejecución. (getint) Puede indicarse un mensaje de solicitud opcional, que facilite al usuario información acerca de lo que se está pidiendo. El mensaje debe ser una cadena de texto y po.r tanto debe estar entre comillas. (getint “¿Cuántos años tienes?:“) Podemos asignar el valor introducido por el usuario a una variable y así utilizarlo después. (setq edad (getint “¿Cuántos años tienes?:“)) (setq edad (+ edad 1)) nos dará la edad que tendrás el próximo año. También puedes crear una variable que contenga el mensaje de solicitud de la función GETINT (setq mens “¿Cuántos años tienes?:“) (setq edad (getint mens)) En este caso mens es una variable que almacena una cadena de texto, pero no es una cadena de texto. Por lo tanto, no debe ir entre comillas. Prueba ahora el siguiente ejemplo:

(setq a 27 mens “¿Cuántos años tienes?:“) (setq edad (getint mens)) Qué pasará si como respuesta a la solicitud de la edad del usuario se introduce !a. Parece lógico que le estamos indicando el valor de la variable a, que contiene el valor numérico 27, de modo que la variable edad debería tomar el valor 27, pero observaras que no es así. Haz la prueba: nos indicará que es “Imposible volver a entrar en LISP.”. Esto es debido a que al ejecutar la función GETINT se está ejecutando el interprete de LISP, y al indicar !a como respuesta estamos diciendo que ejecute el interprete de LISP para obtener el valor asociado a la variable a. Como el interprete de LISP ya está en ejecución, no puede volver a ejecutarse y nos muestra el mensaje anterior.

(GETREAL [mensaje] ) Solicita del usuario un número real. En caso de que el usuario introduzca un número entero, el interprete de AutoLISP lo considerará como un real. Si se introduce cualquier otro tipo de dato que no sea numérico, recordará mediante un mensaje que está solicitando un número real y no finalizará la ejecución de la función hasta que se introduzca un valor numérico, o se pulse ESC para cancelar su ejecución. (getreal) Puede indicarse un mensaje de solicitud opcional, que facilite al usuario información acerca de lo que se está pidiendo. El mensaje debe ser una cadena de texto y por tanto debe estar entre comillas. (setq peso (getreal “¿Cuántos kilos pesas?:“)) (setq peso (- peso 1)) nos dará el peso que tendrás si adelgazas un kilo. También puedes crear una variable que contenga el mensaje de solicitud de la función GETREAL (setq mens “¿Cuántos kilos pesas?:“) (setq peso (getreal mens))

CAPITULO V Funciones de usuario y nuevos comandos

Definir funciones de usuario Hemos visto tan solo algunas de las funciones de AutoLISP, pero también es posible crear nuestras propias funciones. Es más, podemos redefinir las funciones de AutoLISP e incluso los comandos de AutoCAD. La instrucción de AutoLISP que nos permitirá crear nuestras propias funciones, denominadas funciones de usuario, se llama DEFUN.

(DEFUN ( [argumentos] / [variables_locales] ) [expr1] [expr2] …) El primer argumento representa el nombre de la función de usuario que queremos definir. Hay que tener cuidado de no emplear los nombres de las funciones de AutoLISP, ya que en dicho caso se redefinirían. Después de indicar el nombre de la función, se deben indicar entre paréntesis los argumentos y las variables locales, separados por una barra inclinada: 

los argumentos son valores que recibirá la función cuando sea ejecutada por el usuario, o llamada desde otras funciones



las variables locales son aquellas variables que se emplearán tan solo dentro de la función que queremos definir, de modo que restringimos su uso al entorno de la función

Por último, se añaden las expresiones que ejecutará la función. Veamos algunos ejemplos: (defun 2+ ( valor ) (+ valor 2)) En este caso hemos definido una nueva función llamada “2+“, que recibe como argumento un número y realiza la suma de ese número y 2. Las funciones de AutoLISP las ejecutamos escribiendo directamente desde la línea de comandos, bien pues las funciones de usuario se ejecutan exactamente igual. Prueba: (2+ 5) devuelve 7 (2+ 0) devuelve 2 (2+ -2) devuelve 0

Defun devuelve el resultado de la última expresión ejecutada, que en el caso anterior es (+ valor 2). ¿Que sucede si tratamos de ejecutar la función sin pasar ningún argumento, o pasando un argumento que no sea de tipo numérico?. Veamos: (2+ ) indica “; error: argumentos insuficientes” (2+ “texto“) indica “; error: tipo de argumento erróneo: numberp: “texto”" (2+ 1 2)  indica “; error: demasiados argumentos” Podrías pensar que el número indicado se almacena en una variable llamada “valor“, pero no es así. Puedes comprobarlo escribiendo !valor en la línea de comandos, lo que nos devolverá nil. En la función anterior tenemos un argumento y no hay variables locales. La vamos a modificar un poco: (defun 2+ ( valor ) (setq valor (+ valor 2))) Ahora seguro que estas totalmente convencido de que el resultado obtenido se almacena en la variable “valor“. Pues comprobemos si tienes razón: (2+ 5) devuelve 7 !valor devuelve nil La función “2+” recibe como argumento un número, que almacena en la variable “valor“. Pero el ámbito de aplicación es local, es decir una vez que salgamos de la función “2+” la variable “valor” recupera su valor inicial, que en este caso es nil. Vamos con otra prueba… (setq valor 5) devuelve 5 (2+ 4) devuelve 6 ¿Que valor crees que tendrá la variable valor? Pensemos un poco. 

inicialmente valor era nil



al ejecutar (setq valor 5) asignamos a la variable valor el número entero 5



cuando llamamos a la función “2+” con la expresión (2+ 4) tenemos que “valor” es 4



dentro de la función “2+” asignamos (setq valor (+ valor 2)) de modo que “valor” será 6



pero como se trata de una variable local, al salir de la función “2+” recuperamos el valor que tenía la variable “valor” antes de llamar a la función, de modo que valor será 5



por tanto, si tecleamos !valor devolverá 5

Pero, que pasa si ejecutamos (setq valor (2+ 4)). Se repiten los puntos 1-4 anteriores, pero al salir de la función “2+” le asignamos a “valor” el valor devuelto por la función, que es 6. De modo que “valor” será 6. Retoquemos un poco más la función “2+” (defun 2+ ( valor ) (setq inicial valor valor (+ valor 2)))

y borremos el contenido de “valor”. Para ello simplemente le asignamos nil. (setq valor nil) En este caso, dentro de la función “2+” declaramos una variable a la que se le asigna el valor que recibe como argumento la función. (2+ 4) ¿Que valor tendrán ahora las variables “valor” e “inicial“? Vamos a comprobarlo: !valor devuelve nil tal como sucedía en el ejemplo anterior !inicial devuelve 4 Observa que “valor” se comporta como una variable local, solo se emplea dentro de la función. Sin embargo “inicial” es de ámbito global, es decir se sigue empleando al salir de la función. Vamos a modificar un poquito más nuestra función: (defun 2+ ( valor / inicial ) (setq inicial valor valor (+ valor 2))) Ahora hemos añadido la variable “inicial” a la lista de variables locales, con lo que su ámbito será local. Para comprobarlo… !inicial devuelve 4 (setq inicial nil) !inicial devuelve nil (2+ 3) !inicial devuelve nil Bueno, vamos con la última modificación de la función “2+” (defun 2+ ( / valor ) (setq valor (getint “Número: “)) (setq valor (+ valor 2))) Ahora la función “2+” no tiene argumentos, así que para ejecutarla tan solo debemos poner su nombre entre paréntesis: (2+) La variable “valor” es de ámbito local y se emplea GETINT para solicitar un número al usuario. Lo habitual es que se trate de evitar el uso de variables globales innecesarias. De modo que las variables que no sean globales, se deberán añadir a la lista de variables locales dentro de las definiciones de las funciones de usuario.

Crear nuevos comandos de AutoCAD La función de AutoLISP DEFUN no solo permite crear funciones de usuario, también permite crear nuevos comandos de AutoCAD.

Siguiendo con el ejemplo anterior… (defun C:2+ ( / valor ) (setq valor (getint “Número: “)) (setq valor (+ valor 2))) Si anteponemos al nombre de la función a definir “C:” en lugar de crear una función de usuario, se crea un nuevo comando de AutoCAD. La función seguirá funcionando exactamente igual, la única diferencia está en el modo de ejecutarse. Ahora no es necesario poner el nombre de la función entre paréntesis, sino que se escribe directamente en la ventana de comandos de AutoCAD. 2+ También se puede ejecutar poniendo el nombre de la función precedido de “C:” entre paréntesis. (C:2+) En caso de que el nuevo comando creado necesite algún argumento, tan solo podrá ejecutarse del último modo. (defun C:2+ ( valor / inicial ) (setq inicial valor valor (+ valor 2))) (C:2+ 2) devuelve 4 Si escribimos directamente “2+” AutoCAD mostrará el mensaje “error: argumentos insuficientes”. Si tratamos de escribir “2+ 2” en la ventana de comandos de AutoCAD veremos que al escribir el espacio entre el nombre de la función y el argumento, AutoCAD ejecuta lo que cree que es un comando. Por este motivo ahora sólo podríamos ejecutar la función si la llamamos escribiendo (C:2+ 2) en la ventana de comandos de AutoCAD. La función que hemos creado solo nos servirá para la sesión actual de AutoCAD. Es más, tan solo está definida la función 2+ en el dibujo actual, de modo que si hay más dibujos abiertos, en ellos no estará definida. En el siguiente tema veremos como guardar nuestras funciones en archivos de texto, a la vez que comenzamos a trabajar con el entorno de Visual LISP.

CAPITULO VI Introducción al entorno de Visual LISP

Archivos de código fuente en AutoLISP Ya hemos visto que las funciones de AutoLISP se pueden ejecutar directamente desde la ventana de comandos de AutoCAD. Pero el escribir el código directamente en AutoCAD tiene varios inconvenientes, como ya se comentó en el primer artículo del curso: 

el reducido tamaño de la ventana de comandos de AutoCAD



la dificultad de escribir todo el código seguido, sin tabular. Esto es debido a que cada vez que se pulsa Intro, AutoCAD evalúa lo que se ha escrito



el código no se almacena en ningún sitio, así que se perderá al cerrar el dibujo actual o AutoCAD

De modo que en la ventana de comandos de AutoCAD tan sólo se escribirán pequeñas líneas de código que no interese guardar. Suele emplearse por tanto para hacer pruebas y depurar código, aunque también se puede utilizar como una ayuda más para el diseño con AutoCAD. El código de las funciones de AutoLISP se escribe en un editor de textos ASCII, para que así se puedan almacenar. Se puede emplear cualquier editor de texto que permita tan solo códigos ASCII, por ejemplo el bloc de notas de Windows. Otros editores, como MS Word, insertan códigos para diferenciar los estilos de texto, las tabulaciones, etc. y no se pueden emplear para escribir las rutinas de AutoLISP, ya que el interprete de AutoLISP no sabe interpretar esos códigos. Además del bloc de notas, existen muchos otros editores de texto que se pueden emplear para escribir código fuente en AutoLISP. Algunos de estos editores, disponen de utilidades para la programación (permitiendo incluso emplearlos para distintos lenguajes de programación). Otros editores están ya enfocados a la programación en LISP o AutoLISP. Desde la versión 14, AutoCAD incorpora un editor para AutoLISP, en el que tenemos entre otras las siguientes utilidades: 

evaluación de paréntesis durante la programación



posibilidad de compilar el código, consiguiendo así aumentar su velocidad de ejecución



depurador de código específico para AutoLISP con opciones para: Ejecutar el código paso a paso, indicar puntos de parada, evaluar expresiones y valores de variables, etc.



diferenciar el código fuente con distintos colores



tabulado automático del código



utilidades para gestionar proyectos con varios archivos de código



carga de los archivos en AutoCAD

Nosotros seguiremos utilizando la ventana de comandos de AutoCAD para ver como trabajan las funciones de AutoLISP, y nos iremos al editor de Visual Lisp para crear nuevas funciones y comandos. Los archivos de AutoLISP son archivos de texto con extensión LSP.

Nuestra primera función de usuario Antes de comenzar con el editor de Visual LISP, vamos a crear nuestra primera función de usuario: Se trata de una función para convertir un ángulo de radianes a grados decimales, algo que nos será muy útil en muchas de las macros que creemos a lo largo del curso. El código de la función sería el siguiente: (defun RAG ( ang ) (/ (* ang 180.0) pi) ) Hay tres reglas básicas que sigue AutoLISP: 1.

el número de paréntesis de apertura debe ser igual al número de paréntesis de cierre

2.

primero se evaluan las listas más interiores

3.

toda función de AutoLISP devuelve un resultado

El interprete de AutoLISP no evalúa los retornos de carro (Intros), de modo que el código se podía haber escrito todo seguido, en una misma línea: (defun RAG ( ang ) (/ (* ang 180.0) pi) ) Pero así es más difícil de leer. Esta función tiene muy poco código, pero imagina una función mucho mayor escrito toda en la misma línea. Fíjate en que la segunda línea se presenta tabulada, de modo que no comienza a la misma altura que el resto, sino que está desplazada hacia la derecha. Esto nos indica que está incluida dentro de una lista de nivel superior, la de la función defun. No es necesario tabular el código, pero facilita su lectura y además nos permite detectar posibles errores con los paréntesis (Regla número 1). Pi es una constante que ya está definida en AutoLISP, pi equivale al número 3.141592… En la primera línea “(defun RAG ( ang )” definimos la función RAG (Radianes A Grados) que recibe un argumento “ang” (el ángulo en radianes).

Veamos como funciona la segunda línea: Por la Regla número 2, primero se evaluará la lista interior (* ang 180.0) que multiplica el ángulo en radianes “ang” por 180.0 y devuelve el resultado a la lista de nivel superior (recuerda la Regla número 3) que lo divide entre pi = 3.141592… devolviendo a su vez el resultado de la división (el ángulo en grados decimales) a la lista de nivel superior, defun. La última línea cierra la lista de la función defun, cumpliendo así con la Regla número 1. Recordemos la estructura de la función de AutoLISP defun: (DEFUN ( [argumentos] / [variables_locales] ) [expresión1] [expresión2] …) . En la función RAG tenemos un argumento y no se han declarado variables locales (por lo que no es necesario poner el caracter “/“), además solo tenemos una expresión (/ (* ang 180.0) pi) . Como se dijo en el tema anterior, defun devuelve el resultado de la última expresión, que en nuestro caso resulta ser el ángulo ya convertido a grados decimales. Nuestra función RAG se ejecutaría así: (RAG 1.57) siendo 1.57 el ángulo en radianes, y devolvería ese ángulo en grados decimales (aproximadamente 90º). No es necesario poner el nombre de la función RAG en mayúsculas, ya que AutoLISP no diferencia las mayúsculas de las minúsculas (salvo en contadas excepciones al trabajar con textos, como ya veremos). A lo largo de los artículos del curso se mostrarán los nombres de las funciones de usuario en mayúsculas simplemente para que se diferencien perfectamente del resto del código.

Los comentarios en el código Es imprescindible añadir comentarios al código para explicar qué es lo que hace y cómo se hace. En AutoLISP todo lo que en una línea va después de un punto y coma (como este ;) se considera un comentario, de modo que no se evalúa. Da igual poner uno, dos o 45 puntos y comas seguidos, al poner el primero AutoLISP ya sabe que todo lo que está a continuación, en esa línea, es un comentario. Tal vez pienses que no es tan importante añadir explicaciones en el código. Pero si tienes que leer una rutina que creaste hace meses serán muy útiles, por que seguramente no recordaras muy bien como funciona. Además, si alguien va a leer alguna de vuestras rutinas le facilitaras mucho la labor. Al igual que a ti te será más sencillo leer y entender una rutina con abundantes comentarios. Para los comentarios incluidos en el código, se recomienda utilizar el siguiente método: 

;;; Antes del código de las funciones, explicando su funcionamiento



;; En medio del código del programa



; Para explicar una línea de código. A diferencia de las anteriores, esta no se inserta en la columna 1, sino al terminar el código de la línea que comenta

Por ejemplo, la función RAG con comentarios podría quedar así: ;;; Esta función recibe el valor de un ángulo en radianes y lo devuelve en grados decimales.

(defun RAG ( ang ) ; Recibe un ángulo en radianes (/ (* ang 180.0) pi) ;; Multiplica el ángulo en radianes por 180.0 y lo divide por pi. )

El editor de Visual LISP El editor de Visual LISP se inicia desde AutoCAD de varias formas: 

Ejecutando el comando de AutoCAD “VLIDE“.



Desde el menú desplegable de AutoCAD “Herr–>AutoLISP–>Editor de Visual LISP“

Mostrará algo similar a la siguiente imagen:

El editor de Visual LISP

Al abrir el editor veremos dos ventanas, “consola de Visual LISP” y “Rastreo“. De momento no nos vamos a parar a ver para qué sirven o cómo funcionan estas ventanas. Estamos ante un editor de textos, como otro cualquiera, pero con utilidades específicas para la programación en AutoLISP. De modo que para crear un nuevo archivo hacemos lo mismo que en cualquier editor de textos: “Archivo–>Nuevo archivo” (o pulsa Control + N).

Aparece una nueva ventana, tal como puede verse en la siguiente imagen:

Nueva ventana de código en el editor Visual LISP

Es en esta pantalla donde vamos a escribir el código de nuestra rutina RAG, pero antes un pequeño comentario… Es bastante habitual añadir al principio de los archivos de AutoLISP unas líneas de comentarios indicando el nombre del autor, fecha, nombre de los comandos y/o funciones definidas en el archivo, y una breve descripción de estos. De modo que al código anterior le añadiremos unas líneas en la cabecera del archivo: ;;;____________________Eduardo Magdalena____________________;;; ;;;_________________________RAG.LSP___________________________;;; ;;;_______________________Versión 1.0_________________________;;; ;;;________________________21/02/2002_________________________;;; ;;; Esta función recibe el valor de un ángulo en radianes y lo devuelve en grados decimales. (defun RAG ( ang ) ; Recibe un ángulo en radianes (/ (* ang 180.0) pi) ;; Multiplica el ángulo en radianes por 180.0 y lo divide por pi.

) El editor de Visual LISP realiza las tabulaciones automáticamente. Y aunque se pueden eliminar o modificar a nuestro gusto (añadiendo o quitando espacios y tabulaciones), lo recomendable es mantener el formato por defecto para el código. Veamos como queda la función RAG en el editor:

La función RAG en el editor de Visual LISP

En primer lugar observamos que el código tiene distintos colores. Esto es simplemente una ayuda visual para diferenciar los diferentes elementos de nuestras rutinas: 

las funciones de AutoLISP se muestran de color azul setq



los comentarios en morado ;Esto es un comentario



los paréntesis en color rojo (setq a 2.0)



los números en color verde 2.0



los textos en color rosa (en este ejemplo no aparece ningún texto, pero veremos algunos ejemplos más adelante) “Soy un texto“



el resto de elementos, como los nombres de las funciones y de las variables utilizadas, se muestran en color negro

Además, las funciones de usuario se destacarán escribiéndose en mayúsculas RAG.

Este formato de colores se utilizará también en los artículos de este curso, tal como se muestra en el código de la función RAG anterior. El formato coloreado del código se puede desactivar, o se pueden modificar los colores predefinidos para los diferentes elementos. Pero el mantener el formato de colores para el código, nos puede ayudar a detectar errores. Por ejemplo, si ponemos “(setw radianes 1.57)” SETW aparecerá en negro y no en azul, por que la función de AutoLISP es SETQ, de modo que nos indica que hemos escrito mal el nombre de la función. En cuanto a la tabulación, tal vez llame la atención el último paréntesis (el de cierre de la función defun), ya que no está a la misma altura que su paréntesis de apertura. Hay editores para AutoLISP que insertan los paréntesis de cierre a la misma altura que sus correspondientes paréntesis de apertura y hay otros editores que insertan los paréntesis de cierre tabulados, tal como hace (por defecto) el editor de Visual LISP. Una vez escrito el código de la función RAG, tan solo nos queda guardarlo en un archivo. Es recomendable crear un directorio (carpeta) en el que guardar todas nuestras rutinas. Veremos más adelante que se pueden guardar varias funciones y varios comandos en un mismo archivo de AutoLISP, aunque en este caso sólo se ha definido una nueva función de usuario. Se le suele dar a los archivos de AutoLISP el mismo nombre del comando o función que esté definida en él, podemos guardar esta rutina en el archivo “klhsduif.lsp” pero luego no la reconoceríamos. Así que lo mejor será guardar el código de la función RAG en el archivo “RAG.lsp“, dentro del directorio que hemos creado para almacenar nuestras rutinas. Así que selecciona “Archivo –> Guardar como” e indica la ruta y el nombre del archivo. Ahora puedes intentar crear una función llamada GAR que reciba como argumento un ángulo en grados decimales y que devuelva ese ángulo en radianes. Es prácticamente igual a la función que acabamos de ver. Pero recuerda… Antes de empezar a programar (como antes de hacer casi cualquier cosa) conviene pensar en lo que se va a hacer y en cómo lo vamos a hacer. Como esta rutina es muy sencilla no es necesario escribir el pseudocódigo (ni hacer un diagrama de flujo), tan solo hay que pensar un poco en lo que se va a hacer. Guarda el código de la función GAR en un archivo llamado “GAR.lsp” en el directorio donde tengas tus rutinas.

CAPÍTULO VII Cargar los archivos de AutoLISP

Para que la función RAG que creamos en el artículo anterior se pueda ejecutar en AutoCAD, hay que cargar el archivo de AutoLISP en el que está definida. Existen varias formas de cargar los archivos de AutoLISP, pero de momento tan solo vamos a ver tres: 1.

Cargar un archivo desde el editor de Visual LISP

2.

En el menú desplegable de AutoCAD “Herr–>AutoLISP–>Cargar aplicación…“.

3.

Utilizando la función de AutoLISP LOAD.

Si estamos en el editor de Visual LISP y tenemos abierto un archivo con una rutina, para cargarla en AutoCAD podemos: 

Seleccionar en los menús desplegables del Visual LISP “Herramientas –> Cargar texto en editor“.



Pulsando sobre el icono 



Tecleando la combinación de teclas CTRL+ALT+E.

.

Al cargar el archivo, aparece la Consola de Visual LISP mostrando un mensaje parecido al siguiente: “; N formularios cargado de #. En caso de que se produzca algún error en el proceso de carga del archivo, en la consola de Visual LISP se nos indicaría el tipo de error que se ha producido. De modo que habria que modificar el código para corregir ese error antes de cargar la rutina. Una vez cargada la rutina en AutoCAD, podemos probar si funciona. Teclea directamente en la ventana de comandos de AutoCAD: 

(RAG 0)



(RAG pi)



(RAG (/ pi 4))

El segundo método para cargar un archivo de AutoLISP en AutoCAD (seleccionando en el menú de AutoCAD “Herr–>AutoLISP–>Cargar aplicación…“) muestra un letrero de diálogo en el que se selecciona el archivo a cargar y se pulsa el botón “Cargar“.

Cargar / Descargar aplicaciones

Este método ofrece dos ventajas: 

se puede seleccionar más de un archivo



permite descargar las funciones de AutoLISP definidas en los archivos seleccionados

(LOAD archivo [resultado_si_falla]) La función LOAD permite cargar en AutoCAD el archivo de AutoLISP que se indique. Por ejemplo para cargar el archivo RAG sería: (load “rag“) No hace falta indicar la extensión del archivo. Escribiendo así el nombre del archivo solo cargará el archivoRAG.LSP si está en uno de los directorios de soporte de AutoCAD o en el directorio actual. En caso contrario hay que indicar la ruta completa: (load “c:\rutinas\rag.lsp“) Pero si lo hiciéramos así nos daría un error, ya que AutoCAD no reconoce el carácter ”\“, de modo que hay que escribirlo de forma algo especial. Para AutoLISP el caracter “\” hay que indicarlo de cualquiera de esas 2 formas: “\\” o “/“. Por lo tanto escribiremos: (load “c:\\rutinas\\rag.lsp“)

o (load “c:/rutinas/rag.lsp“) En caso de que no se encuentre el archivo, la expresión load puede ejecutar lo que se indique en [resultado_si_falla]. Por ejemplo: (load “rag” (setq test 0)) En este caso, si no se encuentra el archivo RAG.lsp a la variable test se le asigna el valor cero. Suele emplearse para que, cuando se ejecute LOAD desde dentro de una función de AutoLISP, podamos indicarle al usuario que no se ha encontrado el archivo. Como indica el siguiente pseudocódigo: Si test = 0 ==> Mensaje al usuario “Archivo no encontrado” Esto es todo, de momento, sobre la carga de archivos de AutoLISP en AutoCAD. Más adelante veremos otros métodos para cargar nuestras rutinas. Veamos como sería la función GAR propuesta en el tema anterior: ;;;_____________________Eduardo Magdalena_____________________;;; ;;;_________________________GAR.LSP___________________________;;; ;;;_______________________Versión 1.0_________________________;;; ;;;________________________21/02/2002_________________________;;; ;;; Esta función recibe el valor de un ángulo en grados decimales y lo devuelve en radianes. (defun GAR ( ang ) ; Recibe un ángulo en grados decimales (/ (* ang pi) 180.0)  ;; Multiplica el ángulo en grados decimales por Pi y lo divide por 180.0. ) Es muy muy parecida a la función RAG, no?

Crear un directorio para los archivos de AutoLISP Supongamos que el directorio que hemos creado para almacenar nuestras rutinas es “C:\Rutinas“. Veamos como añadirlo a los directorios de soporte de AutoLISP: Inicia AutoCAD. En el menú desplegable “Herr” selecciona “Opciones…“. Así aparece un letrero de diálogo que nos permitirá configurar AutoCAD. En la primera pestaña “Archivos” tenemos una opción denominada “Ruta de búsqueda de archivos de soporte“. Si no está expandida, para ver su contenido pulsamos con el ratón sobre el +que aparece a la izquierda. Para añadir un nuevo directorio de soporte pulsamos el botón “Añadir” que se encuentra a la derecha del cuadro de diálogo. Esto creará una nueva etiqueta, en la que podemos escribir la ruta del directorio o pulsar el botón “Examinar” para seleccionarlo. Ya hemos añadido “C:\Rutinas” a los directorios de soporte de AutoCAD.

Opciones de AutoCAD

También podemos subir o bajar el nuevo directorio en la lista de directorios de soporte. Esto se hace para definir las prioridades, es decir donde buscar primero. De modo que si subimos nuestro directorio hasta el primer lugar (como en la imagen), este será el primer directorio en el que busque AutoCAD.

Operaciones matemáticas en AutoLISP

Hemos visto las operaciones matemáticas básicas: suma, resta, multiplicación y división y las funciones 1+ y 1-. Ahora vamos con otras funciones de AutoLISP que nos permitiran realizar casi cualquier operación matemática en nuestras rutinas.

(ABS numero) Esta función devuelve el valor absoluto del número que se le pase como argumento. Por ejemplo: (abs 23.8) devuelve 23.8 (abs -23.8) también devuelve 23.8 Si el número que recibe como argumento es entero, devuelve un número entero y si es un número real, devuelve un real. (abs -7) devuelve 7 (abs -7.0) devuelve 7.0 (abs 0) devuelve 0

(FIX numero)

Esta función devuelve la parte entera de un número. De modo que devuelve un número entero. Ojo! esta función no redondea, sino que elimina lo que está detrás del punto decimal. (fix 15.8) devuelve 15. (fix -15.8) devuelve -15 (fix 0.99) devuelve 0 (fix -0.99) devuelve 0

(REM numero1 numero2 [numero3] …) Esta función devuelve el resto de la división de numero1 entre numero 2. (rem 2.5 2) devuelve 0.5 (rem 3 2) devuelve 1 Cuando se indican más de 2 números (rem 1 2 3) es equivalente a (rem (rem 1 2) 3). Es decir, primero calcula el resto de la división entre 1 y 2, que es 1, y después lo divide entre 3 y devuelve su resto, que es 1. Si todos los números que recibe como argumentos son enteros, devuelve un número entero y si alguno de ellos es un número real, devuelve un real. (rem 3 2.0) devuelve 1.0

(SIN angulo) Devuelve el seno de un ángulo indicado en radianes. (sin 0) devuelve 0.0 (sin (/ pi 2)) devuelve 1.0

(COS angulo) Funciona igual que la anterior, pero devuelve el coseno del ángulo, que recibe en radianes. (cos 0) devuelve 1.0 (cos pi) devuelve -1.0

(ATAN numero 1 [numero2]) Devuelve el arco cuya tangente sea numero1. Por ejemplo: (atan 0) devuelve 0.0 ya que el ángulo que tiene tangente 0 es el 0.0 Si se indica un segundo número (atan num1 num2) lo que hace es dividir num1 entre num2 y devuelve el arco cuya tangente sea el resultado de la división. Esto se hace para facilitar lo siguiente… (atan (sin angulo) (cos angulo)) devuelve angulo

(SQRT numero) Esta función devuelve la raiz cuadrada del numero que recibe como argumento. Siempre devuelve un número real, no entero. (sqrt 4) devuelve 2.0 (sqrt 2.0) devuelve 1.41…

(EXPT num exp) Devuelve el número num elevado al exponente exp. (expt 2 2) devuelve 4 (expt 2 3) devuelve 8

Si todos los números que recibe como argumentos son enteros, devuelve un número entero y si alguno de ellos es un número real, devuelve un real. (expt 3 2.0) devuelve 9.0

(EXP num) Devuelve el número e ( e = 2.71828… ) elevado al número num. Siempre devuelve un número real. (exp 1) devuelve 2.71828 (exp 2) devuelve 7.38906

(LOG numero) Esta función devuelve el logaritmo neperiano del número que recibe como argumento. (log 1) devuelve 0.0 (log 2) devuelve 0.693147

(GCD entero1 entero2) Esta función recibe dos números enteros y devuelve su máximo común divisor (o denominador). Siempre devuelve un número entero. (gcd 15 5) devuelve 5 (gcd 9 33) devuelve 3

(MAX num1 num2 …) Devuelve el mayor de los números que recibe como argumentos. (max 2 4 1 3 6) devuelve 6 (max 8 4 -9) devuelve 8 Si todos los números que recibe como argumentos son enteros, devuelve un número entero y si alguno de ellos es un número real, devuelve un real. (max 8 4.0 -9) devuelve 8.0

(MIN num1 num2 …) Devuelve el menor de los números que recibe como argumentos. (min 2 3 6) devuelve 2 (min 8 4 -9) devuelve -9 Si todos los números que recibe como argumentos son enteros, devuelve un número entero y si alguno de ellos es un número real, devuelve un real. (min 8 4.0 -9) devuelve -9.0 Pues ya están vistas todas las funciones matemáticas… Enhorabuena!!!

Solicitar textos y puntos al usuario

Recuerdas las funciones GETINT y GETREAL? Nos sirven para solicitar al usuario un número entero y real, respectivamente. Pues la función que se utiliza para solicitar textos al usuario es muy parecida.

(GETSTRING [modo] [mensaje])

Se puede ejecutar sin parámetros (getstring) pero no es recomendable. Se suele indicar un mensaje de texto que explique al usuario lo que se le está solicitando. Por ejemplo: (getstring “¿Cuál es tu nombre?“) Supongamos que te llamas Pepe, a (getstring “¿Cuál es tu nombre?“) responderías Pepe y ya está. Incluso se podría asignar el nombre que indique el usuario a una variable: (setq nombre (getstring “¿Cuál es tu nombre?“)) Pero que sucede si te llamas José Luis? Pues que en cuanto pulses el espacio es como si hubieras pulsado Intro. No nos permite insertar textos con espacios. Para que admita textos con espacios, debemos hacer un pequeño cambio: (setq nombre (getstring T “¿Cuál es tu nombre?”)) Le estamos indicando el argumento [modo] = T. Este argumento puede ser cualquier expresión de AutoLISP, en este caso le pasamos el valor T = Cierto, verdadero. Si no se indica el modo, o si al evaluarlo devuelve nil = Falso, vacío; entonces no admite espacios. Y si se pone cualquier expresión que al evaluarse no devuelva nil, permite espacios. (setq nombre (getstring (+ 1 2) “¿Cuál es tu nombre?“)) permite responder con espacios, ya que (+ 1 2) devuelve3 que es distinto de nil. (setq nombre (getstring (setq var1 nil) “¿Cuál es tu nombre?“)) no permite responder con espacios, ya que (setqvar1 nil) devuelve nil. Para solicitar puntos se utilizan dos funciones que también son parecidas a GETINT y a GETREAL.

(GETPOINT [pto_base] [mensaje]) Esta función le pide un punto al usuario y devuelve una lista con las coordenadas del punto indicado. El usuario puede indicar el punto en pantalla con el ratón o tecleando sus coordenadas, tal y como se haría al dibujar en AutoCAD. Se puede ejecutar sin parámetros (getpoint) pero no es recomendable. Se suele indicar un mensaje de texto que explique al usuario lo que se le está solicitando. Por ejemplo: (getpoint “Punto inicial“) Lo habitual es que además ese punto se almacene en una variable (setq pto (getpoint “Punto inicial“)) Así asignamos a la variable pto algo parecido a lo siguiente: (120.56 135.88 0.0) Veamos ahora para que sirve el argumento opcional [pto_base] aprovechando que tenemos el punto pto definido. (getpoint pto “Siguiente punto:“) Aparece una línea elástica entre el punto pto y la posición del cursor.

(GETCORNER pto_base [mensaje]) Esta función se utiliza también para solicitar puntos al usuario. En este caso el punto base no es opcional, hay que indicarlo. Veamos la diferencia entre las dos expresiones siguientes: (getpoint pto “Siguiente punto:“) (getcorner pto “Siguiente punto:“) Al utilizar getpoint, se muestra una línea elástica entre el punto pto y la posición del cursor. Si se utiliza getcorner, en lugar de una línea elástica, aparece un rectángulo.

Fijémonos un momento en lo que devuelven tanto getpoint como getcorner: (125.68 117.68 0.0). Se trata de una lista, una lista con las corrdenadas de un punto. Bien, pues en el próximo artículo veremos algunas funciones para manejar listas.

Funciones para manejar listas

AutoLISP es un lenguaje de programación basado en listas así que es lógico que el tratamiento que reciban las listas de elementos sea muy bueno. Por otra parte, en AutoCAD nos encontraremos listas continuamente y en todas partes, desde las coordenadas de los puntos como se indicaba en el artículo anterior, hasta los datos de las entidades que son almacenados en la base de datos de AutoCAD. Así que vamos a introducir ahora una serie de funciones para manipular listas de elementos.

(CAR lista) Esta función devuelve el primer elemento de la lista que recibe como argumento. De modo que si (siguiendo con el ejemplo del tema anterior) en la variable pto hemos asignado el valor devuelto por getpoint, tenemos una lista con las coordenadas X, Y y Z del punto designado. Supongamos que pto = (10.0 20.0 0.0). (car pto) devuelve la coordenada X del punto pto. Es decir 10.0

(CDR lista) Esta función devuelve la lista que recibe como argumento pero sin el primer elemento. (cdr pto) devolverá una lista formada por las coordenadas Y y Z del punto pto. Es decir, (20.0 0.0)

(CADR lista) Basándonos en las dos funciones anteriores y recordando que podemos ejecutar listas de instrucciones dentro de otras (a esto le llamamos anidamiento): ¿Cómo se obtendría la coordenada Y del punto pto? Veamos: 

CDR devuelve la lista sin el primer elemento



CAR devuelve el primer elemento de una lista

De modo que (cdr pto) devuelve (Y Z). Así que para obtener la coordenada Y podemos escribir: (car (cdr pto)) devuelve la coordenada Y del punto pto. y, ¿cómo obtenemos la coordenada Z? Pues, volviendo a aplicar la función CDR para llegar a la coordenada Z 

(cdr pto) devuelve (Y Z)



(cdr (cdr pto)) devuelve (Z)

Podrías pensar que escribiendo (cdr (cdr pto)) obtenemos la coordenada Z, sin embargo no es así. La expresión (cdr (cdr pto)) devuelve “una lista” con un único elemento, la coordenada Z del punto pto. Para obtener la coordenada Z del punto pto tenemos que escribir: (car (cdr (cdr pto))) devuelve la coordenada Z del punto pto. En resumen, las coordenadas del punto pto se obtendrian mediante: 

X ==> (car pto)



Y ==> (car (cdr pto))



Z ==> (car (cdr (cdr pto)))

Otras funciones combinando CAR y CDR Si en las expresiones indicadas anteriormente para obtener las coordenadas X, Y y Z ponemos las letras A y D decAr y cDr en mayúsculas, lo anterior quedaría: 

X ==> (cAr pto)



Y ==> (cAr (cDr pto))



Z ==> (cAr (cDr (cDr pto)))

Este ejemplo es simplemente para introducir una simple regla de mnemotecnia ya que las funciones CAR y CDR se pueden agrupar. Para ello, existen una serie de funciones que se denominan juntando las Aes y las Des decAr y cDr respectivamente. El ejemplo anterior, queda: 

X ==> (cAr pto)



Y ==> (cAr (cDr pto)) == (cADr pto)



Z ==> (cAr (cDr (cDr pto))) == (cADDr pto)

Esto nos servirá como regla para recordar el nombre de estas funciones. Debes recordar también que tan solo se permiten 4 niveles de anidación, así que entre la c y la r solo puede haber 4 letras (Aes o Des). Supongamos que tenemos la siguiente lista asignada a la variable lst = ((“a” “b”) (“c” “d”) (“e” “f”)). OJO!! es una lista en la que sus elementos son a su vez listas. Puedes definir la variable lst escribiendo: (setq lst (list (list “a” “b“)  (list “c” “d“)  (list “e” “f“)))

¿Cómo obtendríamos “a” ?? 

(car lst) devuelve (“a” “b”) que es el primer elemento de lst, y “a” es el primer elemento de (“a” “b”) así que:



(car (car lst)) devuelve “a”, o lo que es lo mismo:

(cAAr lst) devuelve “a”

y el elemento “c”? 

(cDr lst) devuelve la lista sin el primer elemento, es decir ((“c” “d”) (“e” “f”)).



Ahora si hacemos (cAr (cDr lst)) devuelve el primer elemento de la lista anterior, es decir (“c” “d”).



Así que (cAr (cAr (cDr lst))) devuelve “c”, o lo que es lo mismo:

(cAADr lst) devuelve “c”

Cómo obtener “d” ? 

(cDr lst) devuelve ((“c” “d”) (“e” “f”))



(cAr (cDr lst)) devuelve el primer elemento de ((“c” “d”) (“e” “f”)), es decir devuelve (“c” “d”)



Si ahora hacemos (cDr (cAr (cDr lst))) obtenemos (“d”), que no es lo mismo que “d”. Ya que se trata de una lista cuyo primer elemento es “d”.



Así que (cAr (cDr (cAr (cDr lst)))) devuelve “d”, o lo que es lo mismo:

(cADADr lst) devuelve “d”

Y cómo obtener “e” ? 

(cDr lst) devuelve ((“c” “d”) (“e” “f”))



y (cDr (cDr lst)) devuelve ((“e” “f”)). Ojo! se trata de una lista cuyo primer (y único) elemento es a su vez otra lista con dos elementos.



Así que (cAr (cDr (cDr lst))) devuelve (“e” “f”), de modo que



(cAr (cAr (cDr (cDr lst)))) devuelve “e”, o lo que es lo mismo:

(cAADDr lst) devuelve “e”

Por último, veamos cómo se obtiene “f”. 

(cAr (cDr (cDr lst))) devuelve (“e” “f”) tal como se vio en el ejemplo anterior.



Así que (cDr (cAr (cDr (cDr lst)))) devuelve (“f”), que os recuerdo que se trata de una lista y que no es lo mismo que “f”.



Por tanto (cAr (cDr (cAr (cDr (cDr lst))))) devuelve “f”.

Podríamos pensar que (cADADDr lst) también devuelve “f”. Pero al ejecutar esta línea AutoCAD nos dice que la función cADADDr no está definida. Ya dijimos antes que se pueden agrupar hasta 4 funciones cAr y cDr, pero aquí estamos intentando agrupar 5, y lógicamente no podemos. Para obtener “f” podríamos escribir, por ejemplo: 

(cAr (cDADDr lst))



(cADADr (cDr lst))

(LENGTH lista) En la variable pto teníamos una lista con las coordenadas de un punto, pero si solo trabajamos en 2D, la coordenada Z no nos interesa. Así que muchas veces los puntos tan sólo tendrán 2 coordenadas (X Y). Pero para un programa no es lo mismo que tenga 2 que 3 coordenadas, a lo mejor va a buscar la coordenada Z y no existe produciéndose un error en nuestra rutina. Así que necesitamos conocer el número de elementos que tienen las listas. Para ello se utiliza la función length, que devuelve el número de elementos de la lista que recibe como argumento. Por ejemplo: (length pto) devuelve 3. y si el punto pto estuviera en 2D (X Y) devolvería 2. Y qué devolvería (length lst) ? siendo lst = ((“a” “b”) (“c” “d”) (“e” “f”)). Pues devolvería 3, ya que lst es una lista con 3 elementos, que a su vez son listas de dos elementos cada una. Qué devolvería (length (car lst)) ? El número de elementos del primer elemento de lst, es decir el número de elementos de (“a” “b”), que es 2.

(LIST elemento1 elemento2 …) Esta función devuelve una lista formada por los elementos indicados. De modo que se utiliza para crear listas. Por ejemplo: (list 1 2 3) devuelve (1 2 3) (list “pepe” 2 “Juan“) devuelve (“pepe” 2 “Juan”) Veamos que hace la siguiente expresión: (list (list “a” “b“) “c” (list “d” “e“) “f“) Recuerda que en AutoLISP cuando nos encontramos con listas anidadas unas dentro de otras, siempre se evalúan primero las listas interiores. 

(list “a” “b“) devuelve (“a” “b”)



(list “d” “e“) devuelve (“d” “e”)

Así que (list (list “a” “b“) “c” (list “d” “e“) “f“) devuelve la siguiente lista ((“a” “b”) “c” (“d” “e”) “f”).

Ejecutar comandos de AutoCAD

Una de las mayores ventajas de la programación en AutoLISP es la posibilidad de ejecutar directamente comandos de AutoCAD en nuestras rutinas.

(COMMAND “comando” [datos])

Esta es la función que nos permite ejecutar comandos de AutoCAD. Hay dos cosas a tener en cuenta con esta función: 

siempre devuelve nil



los nombres de los comandos de AutoCAD, y sus opciones, se indican como textos por lo que van incluidos entre comillas

(command “linea” (list 0.0 0.0) (list 100.0 200.0)) Dibujará una línea desde el origen al punto 100,200. Pero, nos falta algo: Al dibujar líneas en AutoCAD se van indicando puntos y siempre pide “Siguiente punto: ” de modo que para terminar el comando “LINEA” hay que pulsar INTRO. Pues ese Intro también hay que pasárlo a la función command: (command “linea” (list 0.0 0.0) (list 100.0 200.0) “”) Lo realmente potente de COMMAND es que podemos ejecutar casi todos los comandos de AutoCAD. Cuáles no? Son muy pocos, por ejemplo “Nuevo” para empezar un dibujo nuevo. Pero todos los comandos de dibujo, edición, etc se pueden ejecutar. Los datos dependerán del comando de AutoCAD indicado. Por ejemplo para el comando “circulo“, será: (command “circulo” (list 0.0 0.0) 25.0) Esto dibujará una circunferencia de radio 25 con centro en el origen.

Los idiomas de AutoCAD Supongamos que no disponemos de una versión en castellano de AutoCAD, sino que está en inglés, o en Francés, o Chino Mandarín; qué pasa si ejecutamos esto: (command “linea” (list 0.0 0.0) (list 100.0 200.0) “”) AutoCAD no conocerá el comando “linea“, así que nos dará un error. Por suerte se puede solucionar, ya que sino un programa realizado para AutoCAD en Francés sólo serviría para las versiones en Francés. AutoCAD en realidad no “habla” un único idioma, sino que es bilingue, dispone de una lengua que es la que muestra (que corresponde con la versión idiomática del programa: Castellano, Francés, etc) y una lengua interna, el Inglés. De modo que los comandos de AutoCAD (y sus opciones) se pueden escribir en Castellano o en inglés. Pero para diferenciar unos de otros a los comandos en la lengua nativa de AutoCAD (Inglés) se les antepone un guión bajo: (command “_circle” (list 0.0 0.0) 25.0) (command “_line” (list 0.0 0.0) (list 100.0 200.0) “”) Las opciones de los comandos también se deben indicar en inglés anteponiendo un guión bajo. Por ejemplo: (command “_circle” (list 0.0 0.0) “_d” 25.0) Esta línea dibuja una circunferencia de Diámetro 25 con centro en el origen.

Comandos de AutoCAD originales Por otra parte, ya se ha dicho anteriormente que los comandos de AutoCAD se podrían redefinir para que funcionen de forma distinta. Así se podría cambiar el comando “circulo” para que dibuje pentágonos y el comando “linea” para que dibuje circunferencias. Si redefinimos el comando línea para que dibuje circunferencias, entonces deberíamos indicar algo similar a (command “linea” centro radio) en lugar de (command “linea” pt0 pt1″”). Pero cómo haríamos entonces para dibujar una línea? Para ejecutar los comandos originales de AutoCAD, y no los redefinidos (si lo están) debemos anteponer al nombre del comando un punto, por ejemplo:

(command “.circulo” (list 0.0 0.0) 25.0) (command “.linea” (list 0.0 0.0) (list 100.0 200.0) “”) Podemos además indicar los comandos en Inglés anteponiendo un guión bajo así que también los podríamos escribir así: (command “._circle” (list 0.0 0.0) 25.0) (command “_.circle” (list 0.0 0.0) 25.0) Da igual si se pone antes el punto o el guión bajo.

Redefinir un comando de AutoCAD Para redefinir un comando de AutoCAD debemos: 1.

Ejecutar el comando “ANULADEF” (En Inglés “undefine“) indicando el nombre del comando a redefinir. De este modo se elimina la definición del comando de AutoCAD, y la única forma de ejecutarlo será anteponiendo al nombre del comando un punto.

2.

Crear y cargar una macro en la que esté definido un nuevo comando con el nombre del comando que acabamos de anular.

Veamos un ejemplo: 1.

primero anulamos la definición del comando línea. 1.

Podemos hacerlo desde AutoCAD con el comando “ANULADEF” (En inglés “undefine“)

2.

o desde AutoLISP ejecutando: (command “anuladef” “linea“) o (command “_undefine” “_line“)

2.

podemos comprobar que el comando línea ya no funciona 1.

Puedes tratar de ejecutarlo en castellano, en inglés, directamente en AutoCAD o en una expresión de AutoLISP

2. 3.

la única forma de ejecutarlo es anteponiendo a su nombre un punto “.linea“. Iniciamos el editor de Visual LISP: “Herr. –>AutoLISP –> Editor de Visual LISP”

1.

Creamos la macro que se indica a continuación, la guardamos y la cargamos en AutoCAD

(defun C:LINEA ( ) (setq pt (getpoint “Centro del círculo: “)) (setq rad (getreal “Radio del círculo“)) (command “._circle” pt rad) ) Ahora el comando “linea” dibujará círculos. Para recuperar el valor original del comando podemos hacer dos cosas: 1.

cerrar AutoCAD y abrirlo de nuevo, de modo que la macro que hemos creado se borre de la memoria del ordenador

2.

ejecutar el comando de AutoCAD “redefine” (En Inglés es igual, pero con un guión bajo delante) e indicarel nombre del comando del que queremos recuperar su definición original, es decir “linea“.

Bueno, por último un ejercicio: Crear una macro que defina un nuevo comando de Autocad llamado CIRCPERI que dibuje una circunferencia indicando su centro y la longitud de su perímetro.

Operaciones de comparación

En este artículo veremos las funciones de AutoLISP que nos permiten realizar comparaciones, por ejemplo, para ver si algo es mayor que algo, o menor, o si es igual.

(= expr1 expr2 …) Compara si expr1 devuelve el mismo resultado que expr2, en caso afirmativo devuelve T y en caso contrario devuelve nil. (= 5 (+ 1 4)) devuelve T porque (+ 1 4) devuelve 5 (= 5 (+ 1 4.0)) devuelve T aunque (+ 1 4.0) devuelve 5.0 y no 5. Pero 5 y 5.0 valen lo mismo, no? (= 5 5.0) devuelve T No solo podemos evaluar números, también textos: (setq txt1 “Curso de Lisp“) (= txt1 “Curso de Lisp“) devuelve T (= txt1 “Curso de LISP“) devuelve nil. No es lo mismo un texto en mayúsculas que en minúsculas. (= “LISP” “Lisp“) devuelve nil Estamos comparando expresiones, así que: (= (+ 1 5) (/ 12 2)) devuelve T porque ambas expresiones devuelven como resultado 6. La función = puede aceptar más de dos expresiones: (= 6 (+ 1 5) 6.0 (/ 12 2)) devuelve T, pues las cuatro expresiones devuelven 6 o 6.0 (que vale lo mismo).

(/= expr1 expr2 …) Es muy similar a la anterior. Devuelve T si las expresiones no devuelven el mismo valor y devuelve nil si todas las expresiones devuelven el mismo valor. (/= 6 6.0) devuelve nil, porque 6 y 6.0 no son distintos (valen lo mismo). (/= (+ 5 5) (/ 12 2)) devuelve T, pues la primera expresión devuelve 10 y la segunda 6. (/= “LISP” “Lisp“) devuelve T

(< expr1 expr2 …) Compara si expr1 es menor q expr2 ( 3 4) devuelve nil En el ejemplo anterior, como la primera expresión (> 3 4) devuelve nil, ya no se continúan evaluando el resto de expresiones. Cómo hay una expresión que devuelve nil, AND devolverá nil. De modo que la expresión (= 5 5.0) ya no se evalúa. Vamos a complicarlo un poco… Qué devolverá la siguiente expresión? (and (= 5 5.0) ( 3 4)) devuelve nil, porque ambas expresiones son nil (or (= 5 5.0) (> 3 4)) devuelve T, pues la primera expresión se cumple En el ejemplo anterior, como la primera expresión (= 5 5.0) devuelve T, ya no se continúan evaluando el resto de expresiones. Cómo hay una expresión que devuelve T, OR devolverá T. De modo que la expresión (> 3 4) ya no se evalúa. (setq a “Soy otro texto” b 15 c T d nil) (or a b) devuelve T (or c d) devuelve nil (or d d) devuelve nil

(EQUAL expr1 expr2 [precision]) En el tema anterior vimos la función de comparación = que nos sirve para determinar si dos números o textos son iguales. Pero que pasa si queremos comparar otra cosa, por ejemplo dos listas (como dos puntos). (setq pt1 (list 10.0 20.0 0.0) pt2 (list 10.0 (* 10.0 2) 0.0) pt3 (list 9.99 20.0 0.0) pt4 (list 9.99 20.02 0.0)) Al comparar estas listas (puntos) con la función = siempre nos devuelve nil. Aunque pt1 y pt2 sean iguales, y muy parecidos a pt3 y pt4. Por tanto, la función = no nos servirá para comparar listas. Para comparar dos listas se utilizará la función EQUAL. (equal 5 5.0) devuelve T, al igual que la función =, porque 5 vale lo mismo que 5.0 (equal pt1 pt2) devuelve T EQUAL además ofrece un argumento opcional… [precisión]. Veamos como funciona : (equal 4.99 5.0 0.1) devuelve T, porque compara 4.99 y 5 pero con una precisión de 0.1. Así que con esa precisión4.99 == 5.0 Sin embargo, si subimos la precisión… (equal 4.99 5.0 0.001) devuelve nil ¿Y qué pasa con las listas? ¿También podemos indicarle una precisión? Veamos.. (equal pt1 pt3 0.1) devuelve T, porque compara los elementos de las listas (coordenadas) con la precisión que hemos indicado, 0.1 Si subimos la precisión… (equal pt1 pt3 0.001) devuelve nil (equal pt1 pt4 0.01) devuelve nil (equal pt1 pt4 0.02) devuelve T, porque la precisión es 0.02 que es exactamente la diferencia entre 20.02 y 20

El utilizar una precisión muy elevada no implica que todas las comparaciones devuelvan T, todo dependerá de los valores a comparar: (equal 15 20 100) devuelve T (equal 5000 4200 100) devuelve nil

(NOT expr) A esta función le gusta llevarle la contraria a la expresión que recibe como argumento. 

si la expresión devuelve nil, entonces NOT devuelve T.



si la expresión devuelve cualquier cosa que no sea nil, entonces NOT devuelve nil.

(not 5) devuelve nil (not “Texto“) devuelve nil (not (+ 5 1)) devuelve nil Si hacemos… (setq a 5 b nil c T d “Nuevo texto“) (not b) devolverá T, porque b es nil (not c) devolverá nil. Veamos que devuelve la siguiente expresión: (and (not c) 5) Como c es T, (not c) devuelve nil. Al no cumplirse la primera expresión de AND, esta devuelve nil y no continúa evaluando.

Estructuras condicionales simples

Hasta ahora nuestro código ha sido completamente lineal, las líneas de código que escribíamos se ejecutaban una tras otra en el mismo orden. En este tema veremos un tipo de funciones que nos permitirán bifurcar el código, de modo que ya no exista un único camino sino dos o más. Veamos un ejemplo en pseudocódigo: 1.

Introducir el límite inferior

2.

Introducir el límite superior

3.

¿El límite superior es menor que el inferior? 1.

SI–> Mensaje “El límite superior debe ser mayor que el inferior”

2.

NO –> intervalo = límite superior – límite inferior

(IF condición expr_si_cumple [expr_no_cumple]) La función IF es una de las más empleadas al programar. Devuelve el valor de la última expresión evaluada. Si condición es distinto de nil, entonces evalúa la expr_si_cumple. Si condición devuelve nil evalúa laexpr_no_cumple, si existe y si no existe no hace nada. Veamos algunos ejemplos: (if (= 2 2.0) (alert “Los números son iguales“)) La condición a evaluar es: (= 2 2.0) en la que tenemos un número entero 2 y uno real 2.0, pero su valor es el mismo. Aunque sean de distinto tipo 2 y 2.0 valen igual. Así que la condición devuelve T, evaluándose la condición si cumple que muestra un mensaje de alerta. La función IF devuelve el valor de la última expresión evaluada, es decir alert, que es nil. (if (= 2 3) (alert “Los números son iguales“))

En este caso la condición devuelve nil y al no existir expresión no cumple, no haría nada más. ¿Qué valor devolvería IF? El de siempre, el valor de la última expresión evaluada, que en este caso ha sido la propia condición que devuelve nil. De modo que IF devolverá nil. (if  (= 2 3) (alert “Los números son iguales“) (alert “Los números son distintos“) ) En este caso el código se ha escrito en varias líneas y tabulado para facilitar su comprensión. Ahora si tenemos una expresión no cumple, que será evaluada ya que la condición devuelve nil. Veamos el siguiente ejemplo: (setq liminf (getint “\nLímite inferior:“)) (setq limsup (getint “\nLímite superior:”)) (if (> liminf limsup) (alert “El límite superior debe ser mayor que el inferior“) (setq limsup (getint “\nLímite superior:“)) (setqintervalo (- limsup liminf)) (setq intervalo (- limsup liminf)) ) Viendo el código anterior, tal vez pienses que si la condición (> liminf limsup) se cumple, entonces se evaluará la línea siguiente de código completa. Pero no es así, se evalúa la expresión si cumple, que es la primera expresión(alert “El límite superior debe ser mayor que el inferior“). Si la condición no se cumple, devuelve nil, se evaluará la expresión no cumple, que en este caso será (setqlimsup (getint “\nLímite superior“)). Tanto la expresión si cumple, como la no cumple solo pueden ser una única expresión. El ejemplo de código anterior nos daría un error ya que IF no puede tener más que 3 expresiones: 

La condición



La expresión si cumple



La expresión no cumple

(PROGN expr1 expr2 …) Para que se pueda indicar más de una expresión si cumple, o no cumple, en la función IF se suele emplear la función PROGN. El valor devuelto por PROGN es el de la última expresión que recibe como argumento. Esta función en realidad no hace nada, tan solo nos permite agrupar una serie de expresiones. ¿Cómo quedaría el ejemplo anterior? (setq liminf (getint “\nLímite inferior:“)) (setq limsup (getint “\nLímite superior:“)) (if (> liminf limsup) (progn (alert “El límite superior debe ser mayor que el inferior“) (setq limsup (getint “\nLímite superior:“)) (setq intervalo (- limsup liminf)) ) (setq intervalo (- limsup liminf)) )

En este caso la condición si cumple es todo lo siguiente: (progn (alert “El límite superior debe ser mayor que el inferior“) (setq limsup (getint “\nLímite superior:“)) (setq intervalo (- limsup liminf)) ) Si se cumple la condición, se evalúa la condición si cumple, es decir el progn. De modo que se van evaluando las expresiones contenidas en la función PROGN, que devuelve el valor de la última expresión (setq intervalo (- limsup liminf)), que será el valor de la variable intervalo. En caso de que la condición no se cumpla, se evalúa la condición no cumple, (setq intervalo (- limsup liminf)) que curiosamente también devuelve el valor de la variable intervalo. Veamos ahora algunos ejemplos más de estructuras condicionales: (if (and (= 2 2.0) ( Hace que GETdist devuelva la distancia en 2D. Es como si proyectase la distancia real sobre el plano XY.

Getdist solicita una distancia, que se puede escribir directamente, o se pueden indicar dos puntos en pantalla. En este caso, getdist devolverá la distancia real entre esos dos puntos. Si lo que nos interesa obtener es la distancia de sus proyecciones sobre el plano XY actual se añadirá (initget 64) antes de la ejecutar getdist. Por ejemplo: (setq pt1 (getpoint “\nPunto base: “)) (initget 64) (setq dist12 (getdist pt1 “\nSegundo punto: “)) Por último, el código 128 permite indicar una expresión de AutoLISP como respuesta. Por ejemplo, podemos utilizar nuestra rutina RAG (Radianes A Grados decimales) para indicar un ángulo en grados decimales cuando nosotros lo tenemos en radianes. (initget 128) (setq ang (getreal “\nIntroducir ángulo: “)) En este caso el usuario podría indicar como respuesta a la solicitud del ángulo: (RAG (/ pi 4)) Es decir, un ángulo de 45º. Pues llegados a este punto, antes de ver el segundo argumento de (INITGET [modo] [palabras_clave]), es decir, las palabras clave. Vamos a modificar nuestras rutinas ARAND y CIRCPERI. Si revisamos el código de la macro CIRCPERI encontraremos la siguiente línea: (setq pto (getpoint “\nCentro de la circunferencia: “)) ¿deberíamos añadir alguna limitación como respuesta del usuario? Veamos: 

Si el usuario pulsa Intro como respuesta a la variable pto se le asocia el valor nil, que es lo que devolveríaGetpoint. Después al intentar dibujar el círculo (command “_.circle” pto rad) se produciría un

error. Así que debemos impedir que el usuario introduzca Intro como respuesta, de modo que añadiríamos (initget 1) antes de la función getpoint. 

Como estamos solicitando un punto, el código 2 no tiene sentido y lo mismo sucede con el 4.



El código 8 permite indicar un punto fuera de los límites del dibujo, aún cuando estos están activados. Este código si podríamos utilizarlo, aunque si el usuario trabaja con los límites activados, están activados y ya está. Si quiere que los desactive él, no? Porque supongo que los tendrá activados por algún motivo. Así que no le añadimos a initget el código 8.



El código 32 no tiene sentido aquí, pues no aparece la línea base.



Y el código 64 tampoco ya que estamos solicitando un punto, no una distancia.



El código 128 permite introducir como respuesta una expresión de AutoLISP. Este código también se podría indicar, pero lo habitual es no hacerlo. Se podría utilizar en casos en los que el usuario pudiera utilizar una expresión de AutoLISP para calcular el dato. Como en el ejemplo que vimos antes, si tiene un ángulo en radianes y lo tiene que indicar en grados decimales. Aquí nos pide un punto, así que no tiene demasiado sentido.

Definitivamente el código quedaría: (initget 1) (setq pto (getpoint “\nCentro de la circunferencia: “)) Sigamos modificando la rutina CIRCPERI: Unas líneas después de la solicitud del centro de la circunferencia, se solicita su perímetro: (if (setq peri (getdist)) (setq rad (/ peri (* 2 pi))) (setq rad (getvar “circlerad“)) ) ¿Deberíamos añadir la función initget con el código 1 antes de la función getdist? Veamos como funciona esta parte del código: Si el usuario introduce una distancia, se evalúa la condición si cumple del IF es decir: (setq rad (/ peri (* 2 pi))) Y si el usuario pulsa Intro, se asigna a peri el valor nil y evalúa la condición no cumple del IF, es decir: (setq rad (getvar “circlerad“)) Si añadimos antes del getdist la expresión (initget 1), el usuario no podrá indicar Intro, así que



nunca se ejecutaría la expresión no cumple. Por tanto no añadimos el código 1 a Initget. El perímetro del círculo no puede ser ni cero ni un número negativo, de modo que podemos añadir



a initget los códigos 2 y 4. También podríamos añadir el código 64 para que GETdist devuelva la distancia en 2D. Pero



normalmente no conviene añadir este código, excepto cuando la distancia “deba ser” siempre ser en 2D. Una última nota sobre CIRCPERI: Cuando ejecutamos la macro por primera vez en un dibujo en el que no se ha dibujado ningún círculo, la variable de sistema “circlerad” tiene el valor 0.0. En este caso, no ofrecemos la opción de seleccionar el radio del último círculo dibujado pulsando Intro, ya que no existe ningún círculo dibujado previamente. En este caso, no deberíamos indicar el modo 6 a Initget, sino el 7 para que tampoco permita al usuario indicar Intro como respuesta. Veamos como se soluciona en el código completo de la rutina: (defun C:CIRCPERI ( / pto rad peri cmd0 ) (if (= (setq cmd0 (getvar “cmdecho“)) 1)

(setvar “cmdecho” 0) ) (initget 1) (setq pto (getpoint “\nCentro de la circunferencia: “)) (if (= (getvar “circlerad“) 0.0) (progn (prompt “\nPerímetro: “) (initget 7) ) (progn (prompt “\nPerímetro : “) (initget 6) ) ) (if (setq peri (getdist)) (setq rad (/ peri (* 2 pi))) (setq rad (getvar “circlerad“)) ) (command “_.circle” pto rad) (if (= cmd0 1) (setvar “cmdecho” 1) ) (princ) ) (prompt “\nNuevo comando CIRCPERI cargado“) Vamos ahora a modificar la rutina ARAND. La primera solicitud que tenemos en ARAND es la del centro de la arandela. Prácticamente es igual que la solicitud del centro del círculo en CIRCPERI, así que le añadimos también el código 1 a Initget: (initget 1) (setq pto (getpoint “\nCentro de la arandela: “)) A continuación solicita los radios interior y exterior de la arandela. En los que podemos evitar que el usuario indique como respuesta Intro, 0 o un número negativo. Por lo tanto añadiremos Initget con el modo 7. Veamos el código completo: (defun C:ARAND ( / pto rad cmd0 ) (command “_.undo” “_begin“) (if (= (setq cmd0 (getvar “cmdecho“)) 1) (setvar “cmdecho” 0) ) (initget 1) (setq pto (getpoint “\nCentro de la arandela: “)) (initget 7)

(setq rad (getdist pto “\nRadio interior: “)) (command “_.circle” pto rad) (initget 7) (setq rad (getdist pto “\nRadio exterior: “)) (command “_.circle” pto rad) (if (= cmd0 1) (setvar “cmdecho” 1) ) (command “_undo” “_end“) (princ) ) (prompt “\nNuevo comando ARAND cargado“)

Limitar las respuestas de los usuarios (II)

(initget [modo] [palabras_clave]) En el último artículo del curso comenzamos a ver la función INITGET y vimos como funcionaba el argumento[modo] ahora vamos con [palabras_clave]: [palabras_clave] nos permite indicar una serie de textos (palabras) que también serán aceptados como respuesta en la siguiente función de solicitud de datos. Por ejemplo: (initget 7 “Diámetro Perímetro“) (setq rad (getdist “\nRadio del circulo / Diámetro / Perímetro :“)) como respuesta a getdist podemos indicar una distancia, ya sea escribiendo un valor numérico o mediante dos puntos, pero ahora también aceptará como respuesta “Diámetro” y “Perímetro“. El modo 7 impide que se indique como respuesta Intro, cero o un número negativo. 

Si indicamos como respuesta una distancia, asocia esa distancia a la varible rad.



Si indicamos como respuesta “Diámetro“, asocia a la variable rad la cadena de texto “Diámetro“.



Si indicamos como respuesta “Perímetro“, asocia a la variable rad la cadena de texto “Perímetro“.

En [palabras_clave] indicamos una serie de palabras, separadas por espacios, que servirán como respuesta a la siguiente función de solicitud de datos. No hace falta escribir el nombre completo de la palabra, como hicimos antes, basta con que el usuario introduzca como respuesta las letras que aparecen en mayúsculas. Es decir, la D o la P. En el siguiente ejemplo: (initget 7 “Diámetro desHacer“) (setq rad (getdist “\nRadio del circulo / Diámetro / desHacer: “)) Para seleccionar la opción “Diámetro” habrá que escribir al menos la D. Pero para seleccionar “desHacer” al menos habrá que escribir H. También aceptaría “diá” para el diámetro y “desh” para deshacer. Sin embargo no aceptará “de” ni “des” para seleccionar “desHacer“. Fíjate que al menos deben indicarse las letras en mayúsculas. Por otra parte, aceptará tanto “diá” como “dia” sin tilde para seleccionar ”Diámetro“. Supongamos que tenemos el siguiente código: (initget 7 “Diámetro desHacer“)

(setq rad (getdist “\nRadio del circulo: “)) En este caso getdist aceptará como respuestas a la petición del radio de la circunferencia “Diámetro” y “desHacer“. Pero al no indicar estas opciones en el mensaje de getdist, el usuario no sabrá que existen. De modo que es recomendable indicar al usuario las opciones que puede seleccionar: (initget 7 “Diámetro desHacer“) (setq rad (getdist “\nRadio del circulo / Diámetro / desHacer :“)) Como mejor se ve es con un ejemplo así que, vamos a modificar la rutina ARAND añadiendo una opción para indicar el diámetro en lugar del radio. Tenemos que modificar las siguientes líneas: (initget 7) (setq rad (getreal “\nRadio interior: “)) Primero añadimos “Diámetro” a la lista de palabras clave de initget: (initget 7 “Diámetro“) Y a continuación le decimos al usuario que existe una opción llamada “Diámetro” que puede seleccionar como respuesta: (setq rad (getreal “\nRadio interior / Diametro: “)) De esta forma ya hemos añadido la opción “Diámetro” Pero, ¿qué sucede si el usuario indica como respuesta la opción “Diámetro“? Pues que asignamos a la variablerad la cadena de texto “Diámetro” y en la siguiente línea (command “_.circle” pto rad) al intentar dibujar la circunferencia, nos da un error. Así que hay que modificar la rutina añadiendo a continuación algo parecido a: 

Si el usuario selecciona la opción “Diámetro” –> Preguntamos por el diámetro.

Pues esto en AutoLISP, sería: (if (= rad “Diámetro“) (setq rad (getreal “\nDiámetro interior: “)) ) Hay que tener especial cuidado con la condición del IF (= rad “Diámetro“) ya que hay que indicar la palabra clave tal y como aparece en la lista de palabras clave de initget. Es decir, no funcionaría si ponemos (= rad “diámetro“) o (= rad “Diametro“) ya que en el primer caso no ponemos la “D” en mayúsculas y en el segundo no hemos tildado la “a”. Fíjate en lo que hace el código anterior: 

Si el usuario indica una distancia la asigna a la variable rad y luego (if (= rad “Diámetro“)… devuelve nil, puesto que rad es distinto de “Diámetro“.



Si el usuario indica D o Diam, o diámetro, entonces asigna a la variable rad la cadena de texto “Diámetro“. Luego al entrar en el IF, (= rad “Diametro“) devuelve T así que evalúa la expresión si cumple (setq rad (getreal“\nDiámetro interior: “)) que pide un diámetro al usuario y lo asigna a la variable rad.



Pero rad viene de radio, porque en esta variable almacenamos el radio de la circunferencia y no el diámetro. Así que al dibujar la circunferencia (command “_.circle” pto rad) dibujaría una circunferencia del doble del diámetro de lo que ha dicho el usuario.

El código debería ser: (initget 7 “Diámetro“) (setq rad (getreal “\nRadio interior / Diámetro: “)) (if (= rad “Diámetro“)

(setq rad (/ (getreal “\nDiámetro interior: “) 2.0)) ) Pero, ¿Qué pasa si el usuario hace lo siguiente? 

Indica D como respuesta al radio interior.



Indica -50 o 0 como diámetro.

Pues que asignaría a la variable rad el resultado de dividir -50 o 0 entre 2.0. Por tanto tendríamos una circunferencia con radio negativo o cero. Recuerda que initget solo tiene efecto sobre la siguiente función de solicitud de datos, de modo que tenemos que añadir de nuevo la función Initget antes de preguntar por el diámetro: (initget 7 “Diámetro“) (setq rad (getreal “\nRadio interior / Diámetro: “)) (if (= rad “Diámetro“) (progn (initget 7) (setq rad (/ (getreal “\nDiámetro interior: “) 2.0)) ) ) Se ha añadido la función Progn ya que sino, solo se puede ejecutar una expresión como condición si-cumple delIF. Para el radio o diámetro exterior se haría exactamente lo mismo. Por tanto el código completo será: (defun C:ARAND ( / pto rad cmd0 ) (command “_.undo” “_begin“) (if (= (setq cmd0 (getvar “cmdecho“)) 1) (setvar “cmdecho” 0) ) (initget 1) (setq pto (getpoint “\nCentro de la arandela: “)) (initget 7 “Diámetro“) (setq rad (getdist pto”\nRadio interior / Diámetro: “)) (if (= rad “Diámetro“) (progn (initget 7) (setq rad (/ (getreal “\nDiámetro interior: “) 2.0)) ) ) (command “_.circle” pto rad) (initget 7 “Diámetro“) (setq rad (getdist pto “\nRadio exterior / Diámetro: “)) (if (= rad “Diámetro“) (progn (initget 7) (setq rad (/ (getreal “\nDiámetro exterior: “) 2.0)) )

) (command “_.circle” pto rad) (if (= cmd0 1) (setvar “cmdecho” 1) ) (command “_undo” “_end“) (princ) ) (prompt “\nNuevo comando ARAND cargado“)

(GETKWORD [mensaje]) Esta función de AutoLISP se utiliza para obtener una opción indicada por el usuario. Se utiliza en combinación conINITGET, por ejemplo: (initget “DEShacer Nuevo Repetir“) (setq opc (getkword “\nDEShacer / Nuevo / Repetir: “)) En este caso el usuario tan sólo podrá indicar como respuesta una de las palabras clave de la función Initget y se la asigna a la variable opc. No hemos indicado el modo en Initget, tal solo las palabras clave. En la rutina ARAND nos interesaba que apareciera el modo 7 para que no se indique como respuesta Intro, 0 o un número negativo. Pero no es obligatorio indicar siempre un modo. Un ejemplo bastante habitual en las macros es el siguiente: (initget “DEShacer Nuevo Repetir“) (setq opc (getkword “\nDEShacer / Nuevo / Repetir: “)) (cond ((= opc “DEShacer“) (alert “Has seleccionado la opción DEShacer“) ) ((= opc “Nuevo“) (alert “Has seleccionado la opción Nuevo“) ) (T (alert “Has seleccionado la opción Repetir“) ) ) Así en función de la opción que indique el usuario se hace una cosa u otra.

Estructuras repetitivas: Bucles

Hasta hace poco tan sólo podíamos crear programas cuya ejecución fuera lineal: 

Haz esto



Ahora esto otro





Luego vimos las estructuras condicionales IF y COND que ya nos permiten jugar un poco más y hacer que nuestros programas no fueran tan lineales. Ahora vamos a ver funciones que nos permitirá crear repeticiones de código y algo que tal vez te suene, los bucles, que se utilizan mucho en programación.

(WHILE condición [expr1] [expr2] …) La función while ejecuta las expresiones indicadas MIENTRAS se cumpla la condición, y devuelve el valor de la última expresión evaluada. Por ejemplo: (setq i 0) (while (