Está en la página 1de 109

ndice de temas del curso de AutoLISP

Tema 1: Introduccin. Tema 2: Las variables en AutoLISP. Tema 3: Operaciones matemticas bsicas. Tema 4: Solicitar nmeros al usuario. Tema 5: Funciones de usuario y nuevos comandos. Tema 6: Introduccin al entorno de Visual LISP. Tema 7: Cargar los archivos de AutoLISP. Tema 8: Operaciones matemticas. Tema 9: Obtener textos y puntos del usuario. Tema 10: Funciones para manejar listas. Tema 11: Ejecutar comandos de AutoCAD. Tema 12: Operaciones de comparacin. Tema 13: Operaciones lgicas. Tema 14: Estructuras condicionales. Tema 15: Mostrar textos en pantalla. Tema 16: Variables de sistema. Tema 17: Funciones de conversin. Tema 18: Obtener distancias y ngulos del usuario. Tema 19: El comando deshacer en las rutinas de AutoLISP. Tema 20: Funciones de tratamiento de errores. Tema 21: Limitar las respuestas de los usuarios a las funciones de solicitud de datos (I). Tema 22: Limitar las respuestas de los usuarios a las funciones de solicitud de datos (y II). Tema 23: Estructuras repetitivas. Tema 24: Funciones para manipular cadenas de texto. Tema 25: Trabajar con ngulos y distancias. Tema 26: Funciones avanzadas para trabajar con listas. Tema 27: Aplicar funciones a los elementos de las listas. Tema 28: Literales y otras funciones de utilidad. Tema 29: Carga automtica de archivos de AutoLISP. Tema 30: Operaciones con archivos. Tema 31: Leer y escribir archivos de texto. Tema 32: La base de datos de AutoCAD.

curso de AutoLISP Objetivos del curso Con este curso no se pretende formar a expertos programadores en AutoLISP. Ms bien, se pretende acercar este lenguaje de programacin a los usuarios de AutoCAD, para que puedan crear utilidades y pequeas aplicaciones que les ahorren tiempo y clculos en sus trabajos. Otro de los objetivos del curso es el de ver AutoCAD no como un programa de dibujo, sino como una potente base de datos grficos. Y descubrir como funciona esa base de datos de AutoCAD. Es rentable el AutoLISP? Desde luego que puede serlo, puede agilizar y facilitar enormemente el trabajo con AutoCAD. AutoLISP es un lenguaje sencillo de aprender y a diferencia de otros lenguajes de programacin se pueden crear programas interesantes y tiles casi desde el primer momento.

Muchas veces, la mayora del tiempo empleado en crear una rutina o programa se emplea en depurar el cdigo (corregir errores). La planificacin y el anlisis previo de la rutina son de suma importancia para evitarlo. El primer paso para escribir una rutina consiste en escribir en un papel con un lenguaje simple lo que se desea que realice el programa, a esto se le denomina pseudocdigo. Un mtodo algo ms avanzado y eficaz es utilizar diagramas de flujo, puedes ver algo ms sobre esta tcnica en la seccin de artculos: Diagramas de flujo. Cuando ya se empieces a programar en AutoLISP por ti solo, te ser muy til reunir todas las anotaciones sobre proyectos de aplicaciones y rutinas en una libreta o bloc de notas. Interfaces de programacin AutoCAD dispone varios entornos de programacin, la seleccin del tipo de interfaz a emplear para crear una aplicacin depender de las necesidades de la aplicacin y de la experiencia o conocimientos del programador/es:

AutoLISP es una adaptacin del lenguaje de programacin CommonLISP para AutoCAD. Es sencillo de aprender y al mismo tiempo potente. AutoCAD cuenta con un intrprete interno de LISP que permite introducir cdigo desde la lnea de comando 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 aplicacin 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 programacin basado en objetos que utiliza ntegramente la sintaxis del lenguaje Visual Basic y permite usar controles ActiveX. Permite tambin la integracin 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 programacin de lenguaje compilado para el desarrollo de aplicaciones de AutoCAD. El entorno de programacin ObjectARX incluye varias bibliotecas de vnculos dinmicos (DLL) que ofrecen acceso directo a las estructuras de la base de datos, el sistema de grficos y los dispositivos de geometra de AutoCAD.

Caractersticas de AutoLISP El LISP fue desarrollado a finales de los aos 50 por John McCarthy, es el segundo lenguaje de programacin ms antiguo solo precedido por Fortran. Uno de los campos en los que es ms empleado es en la investigacin en inteligencia artificial. Su nombre proviene de LISt Procesing (Procesado de listas), pues se basa en el uso de listas en lugar de datos numricos. Aunque hay quien mantiene que su nombre proviene de "Lost In Stupid Parenthesis". AutoLISP aade al LISP algunas funciones especialmente diseadas para la manipulacin de entidades de AutoCAD. AutoLISP es un lenguaje evaluado. El cdigo se convierte a lenguaje mquina (ceros y unos) y se almacena en la memoria temporal. No es tan lento como los lenguajes interpretados, ni tan rpido como los compilados. En los lenguajes interpretados, se va traduciendo el programa a cdigo mquina (el idioma de los ordenadores) a medida que se ejecuta. En los lenguajes compilados, el cdigo fuente (texto) del programa se traduce a cdigo mquina generando un archivo ejecutable (.EXE) que es el que ejecutar el programa. Hay quien dice que AutoLISP es lento comparndolo con otros lenguajes como Pascal o C, especialmente cuando se utilizan muchas funciones matemticas. Sin embargo, cuando se utiliza manipulando smbolos los resultados son muy distintos. Esto hace de AutoLISP un excelente lenguaje para la programacin de sistemas CAD, ya que un sistema CAD no es ms que un entorno de manipulacin de smbolos grficos. Una de las caractersticas ms importantes del 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, SCP... as como a todas las entidades del dibujo. Esta informacin se puede modificar, extraer e incluso aadir ms entidades al dibujo. La mayora de los lenguajes de programacin 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 informacin de los objetos para determinar de que informacin se dispone, preguntndole a los objetos por ellos mismos. AutoLISP no es estrictamente un lenguaje orientado al objeto,

pero contiene bastantes caractersticas 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 se puede redefinir el comando "POLIGONO" para que dibuje polgonos estrellados en lugar de los regulares. Una mencin especial merece el uso de AutoLISP en la realizacin de programas paramtricos y en el diseo semiautomtico, ya que entre el 60% y el 80% del diseo est dedicado a la modificacin de diseos previos. En los programas paramtricos, el usuario introduce una serie de datos o parmetros a partir de los cuales el programa realiza el diseo completo de un elemento u objeto. Esta es sin duda una de las mayores aplicaciones de AutoLISP. AutoLISP se ha mejorado con la creacin 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 programacin. Adems aade nuevos comandos para permitir la interaccin 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 captulos del curso se trabajar desde la ventana de comandos de AutoCAD. A partir del tercer captulo ya crearemos nuestras rutinas 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 cdigo, es decir escribir el cdigo todo seguido. Esto es debido a que cada vez que se pulsa Intro, AutoCAD evala lo que se ha escrito. En segundo lugar, al terminar la sesin de trabajo en AutoCAD se perdern todos los datos almacenados en la memoria temporal.

Expresiones y procedimientos de evaluacin Un programa en AutoLISP consiste en una serie de expresiones del tipo "(funcin argumentos)". Cada expresin comienza con un parntesis de apertura al que sigue el nombre de una funcin de AutoLISP (o una funcin creada por el usuario) y una serie de argumentos (a veces opcionales) que dependen de la funcin indicada y van separados por al menos un espacio en blanco. Cada expresin termina con un parntesis de cierre, esto es muy importante pues el nmero de parntesis de apertura debe ser igual al de cierre. Cada expresin de AutoLISP devuelve un valor. Un argumento tambin puede ser una expresin, crendose as una estructura formada por expresiones (listas) anidadas unas dentro de otras; de modo que la ms interior devolver su resultado como un argumento a la lista exterior. Cuando existen listas anidadas (unas dentro de otras), primero se evalan las ms 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 funcin + que realiza la suma de los argumentos 1 y 2 devuelve el resultado 3. (+ 31 22 -3) Ejecuta la funcin + que realiza la suma de los argumentos 31, 22 y -3 devuelve el resultado 50. Prueba tambin: (- 17 2) (+ 2.5 22.8) (- 0.25 22.5)

(+ 12 -2 31 -7.5) Ejemplo: (+ (* 2 3) 2) devuelve 8. Primero evala la lista interior y devuelve su resultado a la exterior. (+ 7 (/ 5.0 2) -3) devuelve 6.5. Qu sucedera si al escribir la expresin (+ 1 (/ 5.0 2)) nos olvidsemos de escribir el ltimo parntesis? Haz la prueba, veras que AutoCAD te indica que falta 1 parntesis de cierre. Si el interprete de comandos de AutoCAD encuentra un parntesis de apertura, supone que todo lo que vaya a continuacin hasta el parntesis de cierre es una expresin de AutoLISP. De modo que enva esa expresin al interprete de AutoLISP para que la evale. En el tema 2 veremos algunas de las operaciones matemticas que se pueden realizar con AutoLISP. Tipos de objetos y datos Los elementos de las expresiones de AutoLISP pueden ser smbolos, valores concretos y tambin otras expresiones. Se pueden distinguir los siguientes tipos de elementos:


Ejemplo:

Smbolos: Cualquier elemento que no sea un valor concreto. Por ejemplo una variable, una funcin. Enteros: Nmeros enteros comprendidos entre -32000 y 32000. Reales: Nmeros reales (180 es un nmero entero, pero 180.0 es un nmero real). Cadenas de texto: Texto con una longitud mxima 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.

(+ 5 2) devuelve 7. (+ 5 2.0) devuelve 7.0. En el primer caso todos los argumentos son nmeros enteros, y el resultado de su suma es un nmero entero. En el segundo caso, tenemos un nmero real, as que el resultado es un nmero real. Para que comprendas la importancia de esta distincin, 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 lnea de 36 mm de longitud en la direccin que indicaba el cursor. Prueba ahora indicando el radio de la circunferencia (- 70 25) al utilizar el comando llamado "CIRCULO" (mal llamado, debera ser "circunferencia").

Podemos emplear tambin la constante PI = 3.14159... para realizar clculos. Por ejemplo, ejecuta de nuevo el comando "CIRCULO" y cuando pregunte el radio de la circunferencia, teclea (/ 6 (* 2 PI)). Obtendremos una circunferencia de permetro 6. Notacin empleada Para describir las funciones de AutoLISP que se expliquen a lo largo del curso se seguir la siguiente notacin: (FUNCIN Argumentos_necesarios [Argumentos_opcionales] ) Los nombres de las funciones de AutoLISP aparecen en color azul y el cdigo a teclear en cursiva. Convenciones recomendadas En este apartado se indicarn una serie de convenciones recomendables a la hora de programar. Alguna de ellas puede que an no las entiendas, pero no te preocupes porque las iremos recordando a medida que avancemos en el curso.

Para los comentarios incluidos en el cdigo, se recomienda utilizar el siguiente mtodo: o ;;; Antes del cdigo de las funciones, explicando su funcionamiento. o ;; En medio del cdigo del programa. o ; Para explicar una lnea de cdigo. A diferencia de las anteriores, esta no se inserta en la columna 1, se insertar al terminar el cdigo de la lnea que comenta. Es muy recomendable utilizar un formato tabulado para el cdigo. Evitar el uso de variables globales innecesarias. Utilizar los comandos de AutoCAD y sus opciones en Ingls y precedidos por "._" No abusar de la funcin "SETQ". No utilizar T, MIN, MAX, LAST como smbolos (nombres de variables y funciones). Recuperar el valor inicial de las variables de AutoCAD que han sido modificadas. Aadir unas lneas al final del programa para indicar el nombre del nuevo comando, autor... No introducir demasiado cdigo en la funcin principal. Incluir una funcin de tratamiento de errores. Evitar que el usuario pueda introducir datos errneos. 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.

Qu es una variable? Que no sabes que es una variable? Recuerdas cuando en la escuela te decan "Tengo 3 melones, le doy uno a Juan y despus de comprar otros 2, me com uno porque tena hambre". Pues los melones son una variable. Nosotros hacamos: 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 ms que un valor que vara (o puede hacerlo) a lo largo del tiempo. Pero podamos haber llamado a la variable "y"o "z", y por qu no "melones"? (SETQ Variable1 Valor1 [Variable2 Valor2 ... ] ) En el ejemplo anterior logramos dibujar una circunferencia de 6 unidades de permetro, pero que sucede si queremos utilizar el valor obtenido por (/ 6 (* 2 PI)) para realizar otra operacin, tendremos que volver a teclear la expresin anterior. Existe alguna opcin para almacenar en memoria los resultados de una operacin, tal como hace una calculadora? Desde luego que si, AutoLISP permite almacenar miles de datos en variables, que se almacenan en la memoria del ordenador. La funcin de AutoLISP empleada para definir variables y asignarles un valor es SETQ y permite definir varias variables en una misma expresin. La funcin SETQ devuelve el valor de la ltima variable definida.

Con respecto a los nombres utilizados para designar a los smbolos creados por el usuario (variables y funciones):

No son sensibles a las maysculas. Es lo mismo bloque, BLOQUE y Bloque. No pueden contener espacios en blanco, ni los caracteres " ( ) . ; ' Pueden contener nmeros, 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 lmite en cuanto al nmero de caracteres, pero es recomendable emplear nombre cortos y descriptivos. No es necesario declarar previamente las variables, como sucede en otros lenguajes de programacin. En AutoLISP una misma variable puede contener primero un nmero entero, luego uno real, despus una cadena de texto, etc.

Ejemplo: (SETQ a 3) Esta expresin crea la variable a y le asigna el valor 3. Devuelve el valor de la variable a. Que valor crees que devolver (SETQ b (+ 1 3) melones 23.0)? Fjate que se han definido dos variables b y melones de valor 4 y 23.0 respectivamente. Como melones ha sido la ltima variable evaluada, la expresin anterior devuelve su valor. Si el interprete de comandos de AutoCAD recibe una palabra precedida por el signo de exclamacin "!", comprobar si ese termino ha sido empleado como un smbolo (nombre de variable o una funcin de usuario) de AutoLISP. En cuyo caso devolvera su valor y en caso contrario devolvera nil, que es lo mismo que nada o vaco. Por ejemplo: !a devuelve 3. Prueba tambin las siguientes expresiones y comprueba los valores asignados a las variables: (SETQ c (+ b 3)) Los valores de las variables tambin 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 de b o seguir con el anterior? Al definir la variable d se le asigno el valor que tena la variable b en ese momento, no se estableci ninguna relacin entre ambas variables. Tambin se puede asignar valores no numricos a las variables. Por ejemplo, una cadena de texto. Las cadenas de texto son secuencias de uno o ms caracteres encerrados entre comillas. Dentro de una cadena de texto pueden insertarse una serie de carcteres de control:

\\ representa al smbolo \ \" representa al smbolo " \n representa un cambio de lnea (retorno del carro) \t representa una tabulacin

(SETQ a "\tBienvenido al Curso de AutoLISP" ) En este caso adems, 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 cdigo. Pero tambin supone que se requiera ms cuidado para no pasar a una funcin una variable con un tipo de dato incorrecto. Por ejemplo: (+ a b) Existen una serie de smbolos predefinidos:

T Representa al valor lgico Cierto o Verdadero. nil Representa al valor lgico Falso.

(SETQ b T) (SETQ c nil) En este caso, a la variable a se le ha asignado una cadena de texto, a b el valor Cierto o Verdadero y a c Falso. Para almacenar el radio de la circunferencia de permetro 6 unidades en una variable podemos teclear: (SETQ rad (/ 6 (* 2 PI))) Ejemplo: Por qu las siguientes expresiones estn 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 funcin suma, sino a una variable de valor 23. De modo que el primer termino de la expresin anterior no es una funcin, por lo que da un error. Para recuperar el modo habitual de trabajo de la funcin + es necesario cerrar la sesin actual de AutoCAD e iniciar una nueva sesin.

Operaciones matemticas bsicas En el tema anterior ya vimos algn ejemplo de como funcionan las funciones matemticas bsicas (suma, resta, divisin y multiplicacin). Ahora explicaremos su funcionamiento con mayor profundidad. (+ [nmero1 nmero2 ... ] ) Suma los nmeros indicados como argumentos y devuelve el resultado de dicha suma. Si todos los nmeros de la lista son enteros, el resultado tambin 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 expresin: (SETQ a (+ a 2.5)) devuelve 18.5

Hemos asignado a la variable a el resultado de una operacin en la que usamos el anterior valor asignado a la variable a como uno de los argumentos de la operacin. Si le pasamos a la funcin + un nico nmero como argumento, nos devuelve ese nmero. (+ 12.5) devuelve 12.5 (+ -7.0) devuelve -7.0 Si ejecutamos la funcin suma sin argumentos, devuelve 0. (+ ) devuelve 0 La expresin (+ .5 2) nos dar un error. Los nmeros reales siempre deben comenzar por un nmero entero, incluso si es cero, seguido de la parte decimal. (+ 0.5 2) devuelve 2.5 (+ 3 -0.6) devuelve 2.4 (- [nmero1 nmero2 ... ] ) Resta al primer nmero todos los siguientes nmeros pasados como argumentos. Si todos los nmeros de la lista son enteros, el resultado tambin 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 funcin - un nico nmero como argumento, nos devuelve ese nmero cambiado de signo. (- 12.5) devuelve -12.5 (- -7.0) devuelve 7.0 En la expresin anterior, el primer signo "-" representa a la funcin resta, mientras que el segundo representa el signo de un nmero negativo. Si ejecutamos la funcin resta sin argumentos, devuelve 0. (- ) devuelve 0 (* [nmero1 nmero2 ... ] ) Multiplica los nmeros indicados como argumentos y devuelve el resultado de dicho producto. Si todos los nmeros de la lista son enteros, el resultado tambin 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 expresin: (SETQ a (* a 2.5)) devuelve 270.0 Si le pasamos a la funcin * un nico nmero como argumento, nos devuelve ese nmero. (* 12.5) devuelve 12.5 (* -7.0) devuelve -7.0 Si ejecutamos la funcin * sin argumentos, devuelve 0. (* ) devuelve 0 (/ [nmero1 nmero2 ... ] ) Divide el primer nmero entre el siguiente y devuelve el resultado. Si se pasan ms de dos nmeros como argumentos, el primer nmero se dividir entre el producto de los restantes nmeros. (/ 45 5 3) devuelve 3 (/ 11 5.5) devuelve 2.0 En esta funcin es muy importante recordar que si todos los nmeros de la lista son enteros, el resultado tambin 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 funcin / un nico nmero como argumento, nos devuelve ese nmero. (/ 12.5) devuelve 12.5 (/ -7.0) devuelve -7.0 Si ejecutamos la funcin / sin argumentos, devuelve 0. (/ ) devuelve 0 (1+ <nmero> ) Esta funcin incrementa en una unidad el nmero indicado como argumento. (1+ 5) devuelve 6. Ojo entre "1" y "+" no debe haber ningn espacio, ya que el nombre de la funcin es "1+".

(1+ 2.5) devuelve 3.5 (1+ 0) devuelve 1 (1+ -7) devuelve -6 Una aplicacin bastante habitual de esta funcin es la de incrementar ndices o contadores: (SETQ i 1) (SETQ i (1+ i)) devuelve 2 (1- <nmero> ) Esta funcin reduce en una unidad el nmero indicado. (1- 5) devuelve 4. Ojo entre "1" y "-" no debe haber ningn espacio, ya que el nombre de la funcin es "1-". (1- 2.5) devuelve 1.5 (1- 0) devuelve -1 (1- -1) devuelve -2 Solicitar nmeros al usuario En este apartado veremos dos funciones de AutoLISP que no permitirn solicitar al usuario un nmero entero o real. Esto nos permitir interactuar con el usuario y pedirle informacin. (GETINT [mensaje] ) Solicita del usuario un nmero entero. En caso de que el usuario introduzca un nmero real o cualquier otro dato que no sea un nmero entero, AutoCAD recordar mediante un mensaje que est solicitando un nmero entero y no finalizar la ejecucin de la funcin hasta que se introduzca un valor entero, o se pulse ESC para cancelar su ejecucin. (GETINT) Puede indicarse un mensaje de solicitud opcional, que facilite al usuario informacin acerca de lo que se est pidiendo. El mensaje debe ser una cadena de texto y por tanto debe estar entre comillas. (GETINT "Cuantos aos tienes:") Podemos asignar el valor introducido por el usuario a una variable y as utilizarlo despus. (SETQ edad (GETINT "Cuantos aos tienes:")) (SETQ edad (+ edad 1)) nos dar la edad que tendrs el prximo ao. Tambin puedes crear una variable que contenga el mensaje de solicitud de la funcin GETINT (SETQ mens "Cuantos aos 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 "Cuantos aos tienes:") (SETQ edad (GETINT mens)) Que pasar si como respuesta a la solicitud de la edad del usuario se introduce !a. Parece lgico que le estamos indicando el valor de la variable a, que contiene el valor numrico 27, de modo que la variable edad debera 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 funcin 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 ejecucin, no puede volver a ejecutarse y nos muestra el mensaje anterior. (GETREAL [mensaje] ) Solicita del usuario un nmero real. En caso de que el usuario introduzca un nmero entero, el interprete de AutoLISP lo considerar como un real. Si se introduce cualquier otro tipo de dato que no sea numrico, recordar mediante un mensaje que est solicitando un nmero real y no finalizar la ejecucin de la funcin hasta que se introduzca un valor numrico, o se pulse ESC para cancelar su ejecucin. (GETREAL) Puede indicarse un mensaje de solicitud opcional, que facilite al usuario informacin 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 "Cuantos kilos pesas:")) (SETQ peso (- peso 1)) nos dar el peso que tendrs si adelgazas un kilo. Tambin puedes crear una variable que contenga el mensaje de solicitud de la funcin GETREAL (SETQ mens "Cuantos kilos pesas:") (SETQ peso (GETREAL mens)) Definir funciones de usuario Hemos visto tan solo algunas de las funciones de AutoLISP, pero tambin es posible crear nuestras propias funciones. Es ms, podemos redefinir las funciones de AutoLISP e incluso los comandos de AutoCAD. La instruccin de AutoLISP que nos permitir crear nuestras propias funciones, denominadas funciones de usuario, se llama DEFUN. (DEFUN <funcin> ( [argumentos] / [variables_locales] ) [expr1] [expr2] ...) El primer argumento representa el nombre de la funcon 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 redefiniran. Despus de indicar el nombre de la funcin, se deben indicar entre parntesis los argumentos y las variables locales, separados por una barra inclinada.

Los argumentos son valores que recibir la funcin cuando sea ejecutada por el usuario, o llamada desde otras funciones. Las variables locales son aquellas variables que se emplearn tan solo dentro de la funcin que queremos definir, de modo que restringimos su uso al entorno de la funcin.

Por ltimo, se aaden las expresiones que ejecutar la funcin. Veamos algunos ejemplos: (DEFUN 2+ ( valor ) (+ valor 2))

En este caso hemos definido una nueva funcin llamada "2+", que recibe como argumento un nmero y realiza la suma de ese nmero y 2. Las funciones de AutoLISP las ejecutabamos escribiendo (1+ 5) directamente desde la lnea 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 expresin ejecutada, que en el caso anterior es (+ valor 2). Que sucede si tratamos de ejecutar la funcin sin pasarle ningn argumento, o pasandole un argumento que no sea de tipo numrico?. Veamoslo: (2+ ) indica "; error: argumentos insuficientes" (2+ "texto") indica "; error: tipo de argumento errneo: numberp: "texto"" (2+ 1 2) indica "; error: demasiados argumentos" Podras pensar que el nmero indicado se almacena en una variable llamada "valor", pero no es as. Compruebalo escribiendo !valor en la lnea de comandos, lo que nos devolver nil. En la funcin anterior tenemos un argumento y no hay variables locales. Vamos a modificarla 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 razn: (2+ 5) devuelve 7 !valor devuelve nil Parece que estabas equivocado. La funcin "2+" recibe como argumento un nmero, que almacena en la variable "valor". Pero el mbito de aplicacin es local, es decir una vez que salgamos de la funcin "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. 1. 2. 3. 4. 5. Inicialmente valor = nil Al ejecutar (SETQ valor 5) valor = 5 Cuando llamamos a la funcin "2+" tenemos que "valor" = 4 Dentro de la funcin "2+" asignamos (SETQ valor (+ valor 2)) de modo que "valor" = 4 + 2 = 6 Pero al salir de la funcin "2+" recuperamos el valor que tena la variable "valor" antes de llamar a la funcin, valor = 5

Por tanto, si tecleamos !valor devolvera 5. Pero, que pasa si ejecutamos (SETQ valor (2+ 4)). Se repiten los puntos 1-4 anteriores, pero al salir de la funcin "2+" le asignamos a "valor" el valor devuelto por la funcin, que es 6. De modo que "valor" = 6.

Retoquemos un poco ms la funcin "2+" y borremos el contenido de "valor" (DEFUN 2+ ( valor ) (SETQ inicial valor valor (+ valor 2))) (SETQ valor nil) En este caso, dentro de la funcin "2+" declaramos una variable a la que se le asigna el valor que recibe como argumento la funcin. (2+ 4) Que valor tendrn ahora las variables "valor" e "inicial"? Compruebalo: !valor devuelve nil !inicial devuelve 4 Observa que "valor" se comporta como una variable local, solo se emplea dentro de la funcin. Sin embargo "inicial" es de mbito global, es decir sigue empleandose al salir de la funcin. Vamos a modificar un poquito ms nuestra funcin: (DEFUN 2+ ( valor / inicial ) (SETQ inicial valor valor (+ valor 2))) Ahora hemos aadido 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 modificacin de la funcin "2+" (DEFUN 2+ ( / valor ) (SETQ valor (GETINT "Nmero: ")) (SETQ valor (+ valor 2))) Ahora la funcin "2+" no tiene argumentos, as que para ejecutarla tan solo debemos poner su nombre entre parntesis: (2+) La variable "valor" es de mbito local y se emplea GETINT para solicitar un nmero 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 deberan aadir a la lista de variables locales dentro de las definiciones de las funciones de usuario. Crear nuevos comandos de AutoCAD La funcin de AutoLISP DEFUN no solo nos permite crear funciones de usuario, tambin nos permite crear nuevos comandos de AutoCAD. Siguiendo con el ejemplo anterior...

(DEFUN C:2+ ( / valor ) (SETQ valor (GETINT "Nmero: ")) (SETQ valor (+ valor 2))) Si anteponemos al nombre de la funcin a definir "C:" en lugar de crear una funcin de usuario, se crea un nuevo comando de AutoCAD. La funcin seguir funcionando exactamente igual, la nica diferencia est en el modo de ejecutarse. Ahora no es necesario poner el nombre de la funcin entre parntesis, sino que se escribe directamente. 2+ Tambin puede ejecutarse poniendo el nombre de la funcin precedido de "C:" entre parntesis. (C:2+) En caso de que el nuevo comando creado necesite algn argumento, tan solo podr ejecutarse del ltimo modo. (DEFUN C:2+ ( valor / inicial ) (SETQ inicial valor valor (+ valor 2))) (C:2+ 2) devuelve 4 La funcin que hemos creado solo nos servir para la sesin actual de AutoCAD. Es ms, tan solo est definida la funcin 2+ en el dibujo actual, de modo que si hay ms dibujos abiertos, en ellos no est 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. Archivos de cdigo 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 cdigo directamente en AutoCAD tiene varios inconvenientes, como ya dijimos en el primer tema del curso: 1. 2. 3. El reducido tamao de la ventana de comandos de AutoCAD. La dificultad de escribir todo el cdigo seguido, sin tabular. Esto es debido a que cada vez que se pulsa Intro, AutoCAD evala lo que se ha escrito. El cdigo no se almacena en ningn sitio, as que se perder al cerrar el dibujo actual o AutoCAD.

De modo que en la ventana de comandos de AutoCAD tan solo se escribiran pequeas lneas de cdigo que no interese guardar. Suele emplearse por tanto para hacer pruebas y depurar cdigo, aunque tambin se puede utilizar como una ayuda ms para el diseo con AutoCAD. El cdigo 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 cdigos ASCII, por ejemplo el bloc de notas de Windows. Otros editores, como MS Word, insertan cdigos 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 cdigos. Adems del bloc de notas, existen muchos otros editores de texto que se pueden emplear para escribir cdigo fuente en AutoLISP. Algunos de estos editores, disponen de utilidades para la programacin (permitiendo incluso emplearlos para distintos lenguajes de programacin). Otros editores estn ya enfocados a la programacin en LISP o AutoLISP. Desde la versin 14, AutoCAD incorpora un editor para AutoLISP, en el que tenemos entre otras las siguientes utilidades:

Evaluacin de parntesis durante la programacin. Posibilidad de compilar el cdigo, consiguiendo as aumentar su velocidad de ejecucin. Depurador de cdigo especfico para AutoLISP con opciones para: Ejecutar el cdigo paso a paso, indicar puntos de parada, evaluar expresiones y valores de variables, etc. Diferenciar el cdigo fuente con distintos colores. Tabulado automtico del cdigo.

Utilidades para gestionar proyectos con varios archivos de cdigo. 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 extensin LSP. Nuestra primera funcin de usuario Antes de comenzar con el editor de Visual LISP, vamos a crear nuestra primera funcin de usuario: Se trata de una funcin para convertir un ngulo de radianes a grados decimales. El cdigo de la funcin sera el siguiente: (defun RAG ( ang ) (/ (* ang 180.0) pi) ) El cdigo de las rutinas publicadas en el curso se escribir con el siguiente formato:

Comentarios en el cdigo Funciones de AutoLISP Nmeros Textos FUNCIONES DE USUARIO Y NUEVOS COMANDOS Nombres de variables, parntesis, etc...

Hay tres reglas bsicas que sigue AutoLISP: 1. 2. 3. El nmero de parntesis de apertura debe ser igual al nmero de parntesis de cierre. Primero se evaluan las listas ms interiores. Toda funcin de AutoLISP devuelve un resultado.

El interprete de AutoLISP no evala los retornos de carro (Intros), de modo que el cdigo se poda haber escrito todo seguido, en una misma lnea: (defun RAG ( ang ) (/ (* ang 180.0) pi) ) pero as es ms difcil de leer. Esta funcin tiene muy poco cdigo, pero imaginate una funcin mucho mayor escrita toda en la misma lnea... Fjate en que la segunda lnea 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 funcin defun. No es necesario tabular el cdigo, pero facilita su lectura y adems nos permite detectar posibles errores con los parntesis (Regla nmero 1). Pi es una constante que ya est definida en AutoLISP, pi = 3.141592... En la primera lnea "(defun RAG ( ang )" definimos la funcin RAG (Radianes A Grados) que recibe un argumento "ang" (el ngulo en radianes). Veamos como funciona la segunda lnea: Por la Regla nmero 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 nmero 3) que lo divide entre pi = 3.141592... devolviendo a su vez el resultado de la divisin (el ngulo en grados decimales) a la lista de nivel superior, defun. La ltima lnea cierra la lista de la funcin defun, verificandose as la Regla nmero 1. Recordemos la estructura de la funcin de AutoLISP defun: (DEFUN <funcin> ( [argumentos] / [variables_locales] ) [expresin1] [expresin2] ...) . En la funcin RAG tenemos un argumento y no se han

declarado variables locales (por lo que no es necesario poner el caracter "/"), adems solo tenemos una expresin (/ (* ang 180.0) pi) . Como se dijo en el tema anterior, defun devuelve el resultado de la ltima expresin, que en nuestro caso resulta ser el ngulo ya convertido a grados decimales. Nuestra funcin RAG se ejecutara as: (RAG 1.57) siendo 1.57 el ngulo en radianes, y devolvera ese ngulo en grados decimales (aproximadamente 90). No es necesario poner el nombre de la funcin RAG en maysculas, ya que AutoLISP no diferencia las maysculas de las minsculas (salvo en contadas excepciones que ya explicaremos). Aunque al poner los nombres de las funciones en maysculas se diferencian perfectamente del resto del cdigo. Los comentarios en el cdigo Es imprescindible aadir comentarios al cdigo para explicar que es lo que hace y como se hace. En AutoLISP todo lo que en una lnea va despus de un punto y coma (como este ;) se considera un comentario, de modo que no se evala. Da igual poner uno, dos o 45 punto y comas seguidos, al poner el primero AutoLISP ya sabe que todo lo que est a continuacin, en esa lnea, es un comentario. Tal vez penseis que no es tan importante aadir explicaciones en el cdigo. Pero si teneis que leer una rutina que creasteis hace meses os sern muy tiles, por que seguramente no recordareis muy bien como funciona. Adems, si alguien va a leer alguna de vuestras rutinas le facilitariais mucho la labor. Al igual que a vosotros os ser ms sencillo leer y entender una rutina con abundantes comentarios. Para los comentarios incluidos en el cdigo, se recomienda utilizar el siguiente mtodo:

;;; Antes del cdigo de las funciones, explicando su funcionamiento. ;; En medio del cdigo del programa. ; Para explicar una lnea de cdigo. A diferencia de las anteriores, esta no se inserta en la columna 1, sino al terminar el cdigo de la lnea que comenta.

Por ejemplo, la funcin RAG con comentarios podra quedar as: ;;; Esta funcin 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 "Herr-->AutoLISP-->Editor de Visual Lisp"

Mostrar algo similar a la siguiente imagen:

Al abrir el editor veremos dos ventanas, "consola de Visual LISP" y "Rastreo". De momento no vamos a explicar para que se usan ni como funcionan estas ventanas. Estamos ante un editor de textos, como otro cualquiera, pero con utilidades especficas para la programacin 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:

Es en esta pantalla donde vamos a escribir el cdigo de nuestra rutina RAG, pero antes un pequeo comentario... Es bastante habitual aadir al principio de los archivos de AutoLISP unas lneas de comentarios indicando el nombre del autor, fecha, nombre de los comandos y/o funciones definidas en el archivo, y una breve descripcin de estos. De modo que al cdigo anterior le aadiremos unas lneas en la cabecera del archivo: ;;;________________________MecaniCAD__________________________;;; ;;;_____________http://www.peletash.com/mecanicad_____________;;; ;;;_________________________RAG.LSP___________________________;;; ;;;_______________________Versin 1.0_________________________;;; ;;;________________________21/02/2002_________________________;;; ;;; Esta funcin 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 automticamente. Y aunque se pueden eliminar o modificar a vuestro gusto (aadiendo o quitando espacios y tabulaciones), lo recomendable es mantener el formato por defecto para el cdigo. Veamos como queda la funcin GAR en el editor:

En primer lugar observamos que el cdigo 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. Los comentariosen morado, con gris de fondo. Los nmeros en verde. Etc.

El formato coloreado del cdigo se puede desactivar, o se pueden modificar los colores predefinidos para los diferentes elementos. Pero el mantener el formato de colores para el cdigo, nos puede ayudar a detectar

errores. Por ejemplo, si ponemos "(SETW radianes ...", SETW aparecer en negro y no en azul, por que la funcin de AutoLISP es SETQ, de modo que nos indica que hemos escrito mal el nombre de la funcin. En cuanto a la tabulacin, tal vez llame la atencin el ltimo parntesis (el de cierre de la funcin defun), ya que no est a la misma altura que su parntesis de apertura. Hay editores para AutoLISP que insertan los parntesis de cierre a la misma altura que sus correspondientes parntesis de apertura y hay otros editores que insertan los parntesis de cierre tabulados, tal como hace (por defecto) el editor de Visual LISP. Una vez escrito el cdigo de la funcin RAG, tan solo nos queda guardarlo en un archivo. Es recomendable crear un directorio (carpeta) en el que guardar todas nuestras rutinas. Se le suele dar a los archivos de AutoLISP el mismo nombre del comando o funcin que est definida en l, aunque podemos guardar esta rutina en el archivo "klhsduif.lsp" luego no la reconoceriamos. As que lo mejor ser guardar el cdigo de la funcin GAR 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 funcin llamada GAR que reciba como argumento un ngulo en grados decimales y que devuelva ese ngulo en radianes. Es practicamente igual a la funcin que acabamos de ver. Pero recuerda... Antes de empezar a programar que hay q hacer? Pues pensar en lo que hay que hacer y en como lo vamos a hacer. Como esta rutina es muy sencilla no es necesario escribir el pseudocdigo (ni hacer un diagrama de flujo), tan solo hay que pensar un poco en lo que se va a hacer. Guarda el cdigo de la funcin GAR en un archivo llamado "GAR.lsp" en el directorio donde tengas tus rutinas. Cargar los archivos de AutoLISP Para que la funcin RAG que creamos en el tema 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. 2. 3. Cargar un archivo desde el editor de Visual LISP. En el men desplegable de AutoCAD "Herr-->AutoLISP-->Cargar". Utilizando la funcin 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 mens desplegables del Visual LISP "Herramientas --> Cargar texto en editor". Pulsando sobre el icono .

Tecleando la combinacin 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 #<editor "RUTA/rag.lsp">. En caso de que se produzca algn error en el proceso de carga del archivo, en la consola de Visual LISP se nos indicara el tipo de error que se ha producido. De modo que habria que modificar el cdigo 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 mtodo para cargar un archivo de AutoLISP en AutoCAD (seleccionando en el men de AutoCAD "Herr-->AutoLISP-->Cargar") muestra un letrero de dilogo en el que se selecciona el archivo a cargar (tambin se pueden seleccionar ms de un archivo) y se pulsa el botn "Cargar".

(LOAD archivo [resultado_si_falla]) La funcin LOAD permite cargar en AutoCAD el archivo de AutoLISP que se indique. Por ejemplo para cargar el archivo RAG sera: (load "rag") No hace falta indicar la extensin del archivo. Escribiendo as el nombre del archivo solo cargar el archivo RAG.LSP si est en uno de los directorios de soporte de AutoCAD o en el directorio actual. En caso contrario hay q indicar la ruta completa: (load "c:\rutinas\rag.lsp") Pero esto nos dara un error, ya que AutoCAD no reconoce el caracter "\", 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: (load "c:\\rutinas\\rag.lsp") o (load "c:/rutinas/rag.lsp")

En caso de que no se encuentre el archivo, la expresin 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 un archivo de AutoLISP, podamos indicarle al usuario que no se ha encontrado el archivo. Como indica el siguiente pseudocdigo: Si test = 0 ==> Mensaje al usuario "Archivo no encontrado" Esto es todo, de momento, sobre la carga de archivos de AutoLISP en AutoCAD. Ms adelante veremos mtodos mucho mejores para cargar nuestras rutinas. Veamos como sera la funcin GAR propuesta en el tema anterior: ;;;________________________MecaniCAD__________________________;;; ;;;_____________http://www.peletash.com/mecanicad_____________;;; ;;;_________________________GAR.LSP___________________________;;; ;;;_______________________Versin 1.0_________________________;;; ;;;________________________21/02/2002_________________________;;; ;;; Esta funcin recibe el valor de un ngulo en grados decimales y lo devuelve en radianes. (defun GAR ( ang ) (/ (* ang pi) 180.0) ) Es muy muy parecida a la funcin 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 aadirlo a los directorios de soporte de AutoLISP: Inicia AutoCAD. En el men desplegable "Herr" selecciona "Opciones". As aparece un letrero de dilogo que nos permitir configurar AutoCAD. En la primera pestaa "Archivos" tenemos una opcin denominada "Ruta de bsqueda de archivos de soporte". Si no est expandida, para ver su contenido pulsamos con el ratn sobre el + que aparece a la izquierda. Para aadir un nuevo directorio de soporte pulsamos el botn "Aadir" que se encuentra a la derecha del cuadro de dilogo. Esto crear una nueva etiqueta, en la que podemos escribir la ruta del directorio o pulsar el botn "Examinar" para seleccionarlo. Ya hemos aadido "C:\Rutinas" a los directorios de soporte de AutoCAD.

Tambin 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 algo AutoCAD. Operaciones matemticas Hemos visto las operaciones matemticas bsicas: suma, resta, multiplicacin y divisin y las funciones 1+ y 1-. Ahora vamos con otras funciones de AutoLISP que nos permitiran realizar casi cualquier operacin matemtica en nuestras rutinas. (ABS numero) Esta funcin devuelve el valor absoluto del nmero que se le pase como argumento. Por ejemplo: (abs 23.8) devuelve 23.8 (abs -23.8) tb devuelve 23.8 Si el nmero que recibe como argumento es entero, devuelve un nmero entero y si es un nmero real, devuelve un real. (abs -7) devuelve 7 (abs -7.0) devuelve 7.0

(abs 0) devuelve 0 (FIX numero) Esta funcin devuelve la parte entera de un nmero. De modo que devuelve un nmero entero. (fix 15.8) devuelve 15. Ojo! no redondea, sino que elimina lo que est detras del punto decimal. (fix -15.8) devuelve -15 (fix 0.99) devuelve 0 (REM numero1 numero2 [numero3] ...) Esta funcin devuelve el resto de la divisin de numero1 entre numero 2. (rem 2.5 2) devuelve 0.5 (rem 3 2) devuelve 1 Cuando se indica ms de 2 nmeros (rem 1 2 3) es equivalente a (rem (rem 1 2) 3). Es decir, primero calcula el resto de la divisin entre 1 y 2, que es 1, y despus lo divide entre 3 y devuelve su resto, que es 1. Si todos los nmeros que recibe como argumentos son enteros, devuelve un nmero entero y si alguno de ellos es un nmero real, devuelve un real. (rem 3 2) 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 hay que pasarselo 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 nmero (ATAN num1 num2) lo que hace es dividir num1 entre num2 y devuelve el arco cuya tangente sea el resultado de la divisin. Esto se hace para facilitar lo siguiente... (atan (sin angulo) (cos angulo)) devuelve angulo (SQRT numero)

Esta funcin devuelve la raiz cuadrada del numero que recibe como argumento. Siempre devuelve un nmero real, no entero. (sqrt 4) devuelve 2.0 (sqrt 2.0) devuelve 1.41.. (EXPT num exp) Devuelve el nmero num elevado al exponente exp. (expt 2 2) devuelve 4 (expt 2 3) devuelve 8 Si todos los nmeros que recibe como argumentos son enteros, devuelve un nmero entero y si alguno de ellos es un nmero real, devuelve un real. (expt 3 2.0) devuelve 9.0 (EXP num) Devuelve el nmero e (e = 2.71828... ) elevado al nmero num. Siempre devuelve un nmero real. (exp 1) devuelve 2.71828 (exp 2) devuelve 7.38906 (LOG numero) Esta funcin devuelve el logaritmo neperiano del nmero que recibe como argumento. (log 1) devuelve 0.0 (log 2) devuelve 0.693147 (GCD entero1 entero2) Esta funcin recibe dos nmeros enteros y devuelve su mximo comn divisor (o denominador). Siempre devuelve un nmero entero. (gcd 15 5) devuelve 5 (gcd 9 27) devuelve 9 (MAX num1 num2 ...) Devuelve el mayor de los nmeros que recibe como argumentos. (max 2 4 1 3 6) devuelve 6 (max 8 4 -9) devuelve 8 Si todos los nmeros que recibe como argumentos son enteros, devuelve un nmero entero y si alguno de ellos es un nmero real, devuelve un real.

(max 8 4.0 -9) devuelve 8.0 (MIN num1 num2 ...) Devuelve el menor de los nmeros que recibe como argumentos. (min 2 3 6) devuelve 2 (min 8 4 -9) devuelve -9 Si todos los nmeros que recibe como argumentos son enteros, devuelve un nmero entero y si alguno de ellos es un nmero real, devuelve un real. (min 8 4.0 -9) devuelve -9.0 Pues ya estn vistas todas las funciones matemticas... Enhorabuena Solicitar textos al usuario Recuerdas las funciones GETINT y GETREAL? Nos sirven para solicitar al usuario un nmero entero y real, respectivamente. Pues la funcin que se utiliza para solicitar textos al usuario es muy parecida. (GETSTRING [modo] [mensaje]) Se puede ejecutar sin parmetros (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 "Cual es tu nombre?") Supongamos q te llamas Pepe, a (getstring "Cual es tu nombre?") responderias Pepe y ya est. Incluso se podria asignar el nombre que indique el usuario a una variable: (setq nombre (getstring "Cual es tu nombre?")) Pero que sucede si te llamas Jose 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 pequeo cambio: (setq nombre (getstring T "Cual es tu nombre?")) Le estamos indicando el argumento [modo] = T. Este argumento puede ser cualquier expresin de AutoLISP, en este caso le pasamos el valor T = Cierto, verdadero. Si no se indica el modo, o si al evaluarlo devuelve nil (nil = Falso, vaco) entonces no admite espacios. Y si se pone cualquier expresin que al evaluarse no devuelva nil, permite espacios. (setq nombre (getstring (+ 1 2) "Cual es tu nombre?")) permite responder con espacios, ya que (+ 1 2) devuelve 3 que es distinto de nil. (setq nombre (getstring (setq var1 nil) "Cual es tu nombre?")) no permite responder con espacios, ya que (setq var1 nil) devuelve nil. Solicitar puntos al usuario Para solicitar puntos se utilizan dos funciones que tambin son parecidas a GETINT y a GETREAL. (GETPOINT [pto_base] [mensaje])

Esta funcin 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 digitalizador (ratn) o tecleando sus coordenadas, tal y como se hara al dibujar en AutoCAD. Se puede ejecutar sin parmetros (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 adems 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 lnea elstica entre el punto pto y la posicin del cursor. (GETCORNER pto_base [mensaje]) Esta funcin se utiliza tambin 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 lnea elstica entre el punto pto y la posicin del cursor. Si se utiliza getcorner, en lugar de una lnea elstica, aparece un rectngulo. Fijemonos un momento en lo que devuelven tanto getpoint como getcorner: (125.68 117.68 0.0). Se trata de una lista. En el prximo tema veremos algunas funciones para manejar listas. Funciones para manejar listas AutoLISP es un lenguaje de programacin basado en listas, as que es lgico que el tratamiento que reciban las listas de elementos sea muy bueno. Vamos a ver ahora una serie de funciones para manipular listas de elementos. (CAR lista) Esta funcin 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 funcin 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) y otras Cmo se obtendra la coordenada Y del punto pto??? Veamos:

CDR devuelve la lista sinel 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: (car (cdr pto)) devuelve la coordenada Y del punto pto. y como obtenemos la coordenada Z?? (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)))

Si en las coordenadas X, Y y Z ponemos las letras A y D de cAr y cDr en maysculas, lo anterior quedara:

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

Las funciones CAR y CDR se pueden agrupar. Para ello, existen una serie de funciones que se denominan juntando las Aes y las Des de cAr 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 mnemotcnica para recordar el nombre de estas funciones. Tan solo se permiten 4 niveles de anidacin, 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 q sus elementos son a su vez listas. Como obtendramos 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 ((c d) (e f)). Ahora si hacemos (cAr (cDr lst)) devuelve (c d). As que: (cAr (cAr (cDr lst))) devuelve c, o lo que es lo mismo: (cAADr lst) devuelve c

Cmo obtener d ?? (cDr lst) devuelve ((c d) (e f)) y (cAr (cDr lst)) 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 c Y cmo 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), y para obtener e: (cAr (cAr (cDr (cDr lst)))) devuelve e, o lo que es lo mismo: (cAADDr lst) devuelve e Por ltimo, veamos cmo 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 no es lo mismo que f. Por tanto: (cAr (cDr (cAr (cDr (cDr lst))))) devuelve f. Podriamos pensar que: (cADADDr lst) tambin devuelve f. Pero al ejecutar esta lnea AutoCAD nos dice que la funcin cADADDr no est definida. Ya dijimos antes que se pueden agrupar hasta 4 funciones cAr y cDr, pero aqui estamos intentando agrupar 5, y logicamente no podemos. Para obtener f podriamos hacer, por ejemplo: (cAr (cDADDr lst)) o (cADADr (cDr lst)) (LENGTH lista) En la variable pto teniamos una lista con las coordenadas de un punto, pero si solo trabajamos en 2D, la Z no nos interesa. As que muchas veces los puntos tan solo tendran 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 produciendose un error en nuestra rutina. As que necesitamos conocer el nmero de elementos que tienen las listas. Para ello se utiliza la funcin length, que devuelve el nmero de elementos de la lista que recibe como argumento. Por ejemplo: (length pto) devuelve 3. y si el pto estuvuiera en 2D (X Y) devolvera 2. Y qu devolvera (length lst) ??? siendo lst = ((a b) (c d) (e f)). Pues devolvera 3, ya que lst es una lista con 3 elementos que a su vez son listas. Qu devolvera (length (car lst)) ??? El nmero de elementos del primer elemento de lst, es decir el nmero de elementos de (a b), que es 2. (LIST elemento1 elemento2 ...) Esta funcin 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 expresin: (list (list "a" "b") "c" (list "d" "e") "f") Veamos (list "a" "b") devuelve ("a" "b") y (list "d" "e") devuelve ("d" "e"). As que (list (list "a" "b") "c" (list "d" "e") "f") devuelve (("a" "b") "c" ("d" "e") "f"). Ejecutar comandos de AutoCAD Una de las mayoresventajas de la programacin en AutoLISP es la posibilidad de ejecutar comandos de AutoCAD en nuestras rutinas. (COMMAND "comando" [datos]) Esta es la funcin que nos permite ejecutar comandos de AutoCAD. Hay que destacar que 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 lnea desde el origen al punto 100,200. Pero, nos falta algo: Al dibujar lneas 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 tambin hay que pasarselo a command: (command "linea" (list 0.0 0.0) (list 100.0 200.0) "") o as (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. Cuales no? Son muy pocos, por ejemplo "Nuevo" para empezar un dibujo nuevo. Pero todos los comandos de dibujo, edicin, etc se pueden ejecutar. Los datos dependeran 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 versin en castellano de AutoCAD, sino que est en ingls, o en Francs, o Chino Mandarn... 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 Ingls slo servira para las versiones en Ingls. 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 versin idiomtica del programa: Castellano, Francs, etc) y una lengua interna, el Ingls. De modo que los comandos de AutoCAD (y sus opciones) se pueden escribir en Castellano o en ingls. Pero para diferenciar unos de otros a los comandos en la lengua nativa de AutoCAD (Ingls) se les antepone un guin 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 tambin se deben indicar en ingls anteponiendoles un guin bajo. Por ejemplo: (command "_circle" (list 0.0 0.0) "_d" 25.0) Esta lnea dibuja una circunferencia de Dimetro 25 con centro en el origen. Comandos de AutoCAD redefinidos Por otra parte, ya se ha dicho anteriormente que los comandos de AutoCAD se podrn redefinir para que funcionen de forma distinta. As se puede cambiar el comando "circulo" para que dibuje pentagonos y el comando "linea" para que dibuje circulos. Si redefinimos el comando lnea para que dibuje circulos, entonces debemos indicar (command "linea" centro radio) y no (command "linea" pt0 pt1"") que no funcionara, puesto que le estamos pasando unos parmetros que no espera. Pero cmo hacemos ahora para dibujar una lnea? Para ejecutar los comandos originales de AutoCAD, y no los redefinidos (si lo estn) 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 adems indicar los comandos en Ingls anteponiendoles un guin bajo as que tambin podriamos escribirlo 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 guin bajo. Para redefinir un comando de AutoCAD: 1. 2. Ejecutar el comando "ANULADEF" (En Ingls "undefine") indicando el nombre del comando a redefinir. De este modo se elimina la definicin del comando de AutoCAD, y la nica forma de ejecutarlo ser anteponiendo al nombre del comando un punto. Crear y cargar una rutina en la que est definido un nuevo comando con el nombre del comando que acabamos de anular.

Veamoslo con un ejemplo: Primero anulamos la definicin del comando lnea. Podemos hacerlo desde AutoCAD con el comando "ANULADEF" (En ingls "undefine") o desde AutoLISP: (command "_undefine" "linea") Ahora podemos comprobar que el comando lnea no funciona en AutoCAD, y la nica forma de ejecutarlo es anteponiendo a su nombre un punto ".linea". Cargamos la siguiente funcin: (defun C:LINEA ( ) (setq pt (getpoint "Centro del crculo: ")) (setq rad (getreal "Radio del crculo")) (command "._circle" pt rad) )

Ahora el comando "linea" dibujar crculos. Para recuperar el valor original del comando podemos hacer dos cosas: 1. 2. Cerrar AutoCAD y abrirlo de nuevo de modo que la rutina que hemos creado se borre de la memoria del ordenador. Ejecutar el comando de AutoCAD "redefine" (En Ingls es igual) e indicarle el nombre del comando del que queremos recuperar su definicin original, es decir "linea".

Bueno, por ltimo un ejercicio: Crear un nuevo comando llamado CIRCPERI que dibuje una circunferencia indicando su centro y la longitud de su permetro. Operaciones de comparacin Qu es una operacin de comparacin?? Pues comparar, por ejemplo, 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 ontrario 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 nmeros, tambin 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 maysculas que en minsculas. (= "LISP" "Lisp") devuelve nil Estamos comparando expresiones, as que: (= (+ 1 5) (/ 12 2)) devuelve T porque ambas expresiones devuelven como resultado 6. La funcin = puede aceptar ms 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 expresin devuelve 10 y la segunda 6. (/= "LISP" "Lisp") devuelve T

(< expr1 expr2 ...) Compara si expr1 es menor q expr2 (< 4 5) devuelve T, ya que 4 es menor que 5 (< 4 -5) devuelve nil (< 5 5.0) devuelve nil Si se ponen ms de 2 expresiones, se comprueba que esten ordenadas de menor a mayor y devuelve T si lo estn y nil si no lo estn. (< 1 2 3) devuelve T (< 1 2 0) devuelve nil (< (+ 1 2) (* 2 2.0) (/ 12 2)) devuelve T Tambin podemos comparar textos. El interprete de AutoLISP evalua los cdigos ASCII de los textos. Es decir los ordena alfabticamente de la "a" a la "z". (< "Albacete" "Barcelona") devuelve T (< "a" "c") devuelve T (< "d" "c") devuelve nil (< "C" "c") devuelve T, puesto que los cdigos ASCII de la maysculas son inferiores a los de las minsculas. Los cdigos ASCII, son una serie de nmeros que se asignaron a las letras del alfabeto y a algunos de los caracteres ms usuales (al menos en Occidente). El caracter "a" tiene asociado el cdigo 97, la "b" el 98, etc hasta la "z". El caracter "A" tiene asociado el cdigo 65, la "B" el 66, etc hasta la "Z". (> expr1 expr2 ...) Supongo que ya os imaginais como funciona, no? Comprueba si las expresiones estn ordenadas de mayor a menor. (> 5 3) devuelve T (> -2 3) devuelve nil (> 5 2 3) devuelve nil (> 5 4 3) devuelve T Y ahora con los textos: (> "a" "c") devuelve nil, pues el cdigo ASCII de "a" es menor que el de "c". (<= expr1 expr2 ...) Comptrueban si las expresiones son menores o iguales que las anteriores.

(<= 1 2 2.0 3) devuelve T (<= 1 3.0 2 3) devuelve nil (>= expr1 expr2 ...) Comptrueban si las expresiones son mayores o iguales que las anteriores (>= 3 2 2.0 1) devuelve T (>= 3 1.0 2 1) devuelve nil (>= 3 3 3 3) devuelve T Veamos como se hara el ejercicio propuesto en el tema anterior: Crear un nuevo comando llamado CIRCPERI que dibuje una circunferencia indicando su centro y la longitud de su permetro. Qu es lo primero que hay que hacer ??? Esta respuesta tiene que ser instintiva, como un acto reflejo: El pseudocdigo. Siempre comenzaremos nuestras rutinas escribiendo el pseudocdigo (o haciendo un diagrama de flujo) de lo que se pretende hacer. Bueno, cmo podria ser el pseudocdigo de esta rutina, vamos a ver: 1. 2. 3. 4. Pedir al usuario el centro de la circunferencia. Pedir al usuario el permetro de la circunferencia. Calcular el radio de la circunferencia a partir de su permetro. Dibujar la circunferencia.

Una vez que terminamos el pseudocdigo, ya tenemos el 80% de la rutina. Si el pseudocdigo es correcto, el resto es de lo ms simple. Primero hay que aadir la definicin del nuevo comando CIRCPERI: (defun C:CIRCPERI ( ) 1) Pedir al usuario el centro de la circunferencia. Podriamos poner (getpoint "Centro de la circunferencia") pero eso no servira de nada, por que no almacenamos el punto que indica el usuario en ningn sitio. Tendriamos que hacer, entonces... (setq pto (getpoint "Centro de la circunferencia")) asi almacenamos el punto indicado en la variable pto. 2) Pedir al usuario el permetro de la circunferencia. Por ejemplo (setq peri (getint "Permetro:")) pero al usar getint, solo permite obtener nmero enteros. As que podriamos cambiarlo por: (setq peri (getreal "Permetro:")) 3) Calcular el radio de la circunferencia a partir de su permetro. Peri = 2* pi * rad as que rad = Peri / ( 2 * pi). Traduciendolo a cdigo: (setq rad (/ peri (* pi 2))) 4) Dibujar la circunferencia (command "_.circle" pto rad) Slo nos falta una cosa. Pista: El nmero de parntesis de apertura tiene que ser igual al nmero de parntesis de cierre. As que:

) El cdigo completo de la rutina sera: (defun C:CIRCPERI ( ) (setq pto (getpoint "Centro de la circunferencia")) (setq peri (getreal "Permetro:")) (setq rad (/ peri (* pi 2))) (command "_.circle" pto rad) ) Tambin deberias aadir comentarios al cdigo y una cabecera con varias lneas de comentarios en el archivo LSP indicando El nombre de la rutina, fecha, autor, etc. Te das cuenta de la importancia del pseudocdigo? Al programar nos ha guiado paso a paso. Operaciones lgicas Exste una serie de funciones de AutoLISP que nos permiten realizar operaciones lgicas. Suelen emplearse en combinacin con las operaciones de comparacin. (AND expr1 expr2 ...) Esta funcin devuelve T si ninguna de las expresiones que recibe como argumento es (devuelve) nil. Si una sola de las expresiones devuelve nil, la funcin AND devolver nil. Es decir, comprueba que se cumplan todas las expresiones que recibe como argumento. (and (< 3 4) (= 5 5.0)) devuelve T, porque las dos expresiones devuelven T (and (> 3 4) (= 5 5.0)) devuelve nil, porque (> 3 4) devuelve nil En el ejemplo anterior, como la primera expresin (> 3 4) devuelve nil, ya no se continuan evaluando el resto de expresiones. Cmo hay una expresin que devuelve nil, AND devolver nil. De modo que la expresin (= 5 5.0) ya no se evala. Vamos a complicarlo un poco... Qu devolver la siguiente expresin? (and (= 5 5.0) (< 3 4) "Soy un texto" 5.8) Preguntndolo de otra forma: Alguna de las expresiones que recibe AND como argumentos es nil? No, as que AND devuelve T.

(setq a "Soy otro texto" b 15 c T d nil) (and a b) devolver T (and a d) (and a b c d) devolver nil, porque d es nil

(OR expr1 expr2 ...) Devuelve nil si todas las expresiones son nil. En caso de que alguna de las expresiones no devuelva nil, OR devuelve T. Es decir, comprueba si alguna de las expresiones se cumple. (or (/= 5 5.0) (> 3 4)) devuelve nil, porque ambas expresiones son nil (or (= 5 5.0) (> 3 4)) devuelve T, pues la primera expresin se cumple En el ejemplo anterior, como la primera expresin (= 5 5.0) devuelve T, ya no se continuan evaluando el resto de expresiones. Cmo hay una expresin que devuelve T, OR devolver T. De modo que la expresin (> 3 4) ya no se evala. (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 funcin de comparacin = que nos sirve para determinar si dos nmeros 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 funcin = siempre nos devuelve nil. Aunque pt1 y pt2 sean iguales, y muy parecidos a pt3 y pt4. Por tanto, la funcin = no nos servir para comparar listas. Para comparar dos listas se utilizar la funcin EQUAL. (equal 5 5.0) devuelve T, al igual q la funcin =, porque 5 vale lo mismo que 5.0 (equal pt1 pt2) devuelve T EQUAL adems ofrece un argumento opcional... [precisin]. Veamos como funciona : (equal 4.99 5.0 0.1) devuelve T, porque compara 4.99 y 5 pero con una precisin de 0.1. As que con esa precisin 4.99 == 5.0 Sin embargo, si subimos la precisin... (equal 4.99 5.0 0.001) devuelve nil Y que pasa con las listas? Tambin podemos indicarle una precisin? Veamoslo.. (equal pt1 pt3 0.1) devuelve T, porque compara los elementos de las listas (coordenadas) con la precisin que hemos indicado, 0.1 Si subimos la precisin... (equal pt1 pt3 0.001) devuelve nil (equal pt1 pt4 0.01) devuelve nil

(equal pt1 pt4 0.02) devuelve T, porque la precisin es 0.02 que es exactamente la diferencia entre 20.02 y 20 El utilizar una precisin 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 funcin le gusta llevarle la contraria a la expresin que recibe como argumento.

Si la expresin devuelve nil, entonces NOT devuelve T. Si la expresin 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 expresin: (and (not c) 5) Como c es T, (not c) devuelve nil. Al no cumplirse la primera expresin de AND, esta devuelve nil y no contina evaluando. Mostrar textos en pantalla Hay varias funciones para mostrar textos en pantalla. De momento tan solo vamos a ver un par de ellas, pero habr ms. (PROMPT mensaje) Muestra el texto indicado como argumento en pantalla, y siempre devuelve nil. (prompt "Bienvenidos al Curso") (ALERT mensaje) Muestra el texto que recibe como argumento en un letrero. Tambin devuelve nil. Se utiliza principalmente para avisar al usuario en caso de error, o para mostrarle alguna informacin importante. (alert "Error: Dato no vlido") (TERPRI)

En el tema 12, creamos un nuevo comando para AutoCAD denominado CIRCPERI. Si habeis cargado y ejecutado el comando, habreis observado que los mensajes para solicitar el centro de la circunferencia y su permetro aparecen en la misma lnea y pegados. Para que los mensajes aparezcan en lneas distintas, se puede emplear la funcin TERPRI. Ya que esta funcin mueve el cursor a una nueva lnea en la ventana de comandos de AutoCAD, y devuelve nil. El cdigo de la rutina quedara as: (defun C:CIRCPERI ( ) (setq pto (getpoint "Centro de la circunferencia")) (terpri) (setq peri (getreal "Permetro:")) (terpri) (setq rad (/ peri (* pi 2))) (command "_.circle" pto rad) ) As los mensajes de solicitud aparecern en lneas separadas. Los caracteres de control Creo que ya se dijo que AutoLISP no reconoce una serie de caracteres, por ejemplo al indicar la ruta de un archivo no reconoce el caracter "\" y hay que indicarselo as: "\\". Pues existen ms caracteres de control predefinidos:

\\ Equivale al caracter \ \" Equivale al caracter " \e Equivale a ESCape \n Equivale al retorno de carro \r Equivale a pulsar Intro \t Equivale a pulsar la tecla del tabulador

\" nos permite escribir las comillas dentro de un texto. Por ejemplo: (alert "Esto son un par de comillas \" o no?") La nica comilla q se tiene q ver es la que est escrita as: \" . Las otras nos indican donde comienza y termina el texto, nada ms. (alert "Texto en lnea 1\nTexto en lnea 2") Se pone 1\nTexto todo junto. Para qu poner espacios? (alert "Texto en lnea 1\n Texto en lnea 2") Si lo ponemos as, aade un espacio en blanco al principio de la segunda lnea. Por eso se pone todo seguido. (alert "Texto en lnea 1\n\tTexto en lnea 2") En este ejemplo la segunda lnea est tabulada. Entonces, cmo quedara el cdigo de la rutina CIRCPERI utilizando caracteres de control, en lugar de la funcin (TERPRI)???

(defun C:CIRCPERI ( ) (setq pto (getpoint "\nCentro de la circunferencia")) (setq peri (getreal "\nPermetro:")) (setq rad (/ peri (* pi 2))) (command "_.circle" pto rad) ) Un par de cosas ms con respecto a esta rutina... Cuando se crea un archivo LISP en el que est definido un nuevo comando es bastante til aadir al final de todo el cdigo algo similar a... (prompt "\nNuevo comando CIRCPERI cargado") Esta lnea se pondra despus del parntesis de cierre de defun. Es decir, que cuando se ejecuta CIRCPERI desde AutoCAD esta lnea no se evala. Para qu ponerla entonces?? Pues muy sencillo... para que cuando se cargue el archivo en AutoCAD muestre en pantalla: Nuevo comando CIRCPERI cargado. As el usuario sabe cual es el nombre del comando definido en el archivo que se acaba de cargar. De modo que el mensaje slo se mostrar al cargar el archivo. Por otro lado... si recordamos la estructura de la funcin DEFUN: (DEFUN nombre_funcin ( argumentos / variables_locales ) expr1 expr2 ...) Veremos que en la rutina CIRCPERI no hemos indicado variables locales, as que todas las variables sern globales. Es decir que al ejecutar CIRCPERI y dibujar un crculo, luego nos quedan accesibles los valores de las variables pto peri y rad desde AutoCAD, ocupando y malgastando memoria. As que vamos a ponerlas como locales. Slo habra que cambiar la siguiente lnea... (defun C:CIRCPERI ( / pto peri rad ) OJO! la barra inclinada / hay que ponerla, sino seran argumentos y no variables locales. El cdigo completo de la rutina es el siguiente: ;;;________________________MecaniCAD__________________________;;; ;;;_____________http://www.peletash.com/mecanicad_____________;;; ;;;_______________________CIRCPERI.LSP________________________;;; ;;;_______________________Versin 1.1_________________________;;; ;;;________________________26/02/2002_________________________;;; ;;; Comando para dibujar una circunferencia indicando su centro y la longitud ;;; de su permetro. (defun C:CIRCPERI ( / pto peri rad ) (setq pto (getpoint "\nCentro de la circunferencia")) (setq peri (getreal "\nPermetro:"))

(setq rad (/ peri (* pi 2))) (command "_.circle" pto rad) ) (prompt "\nNuevo comando CIRCPERI cargado") Estructuras condicionales simples Hasta ahora nuestro cdigo ha sido completamente lineal, las lneas de cdigo que escribamos se ejcutaban despus una tras otra en el mismo orden. En este tema veremos un tipo de funciones que nos permitiran bifurcar el cdigo, de modo que ya no exista un nico camino sino dos o ms. Veamos un ejemplo en pseudocdigo: 1. 2. 3. Introducir el lmite inferior Introducir el lmite superior El lmite superior es menor que el inferior? 1. SI--> Mensaje "El lmite superior debe ser mayor que el inferior" 2. NO --> intervalo = lmite superior - lmite inferior

(IF condicin expr_si_cumple [expr_no_cumple]) La funcin IF es una de las ms empleadas al programar. Devuelve el valor de la ltima expresin evaluada. Si condicin es distinto de nil, entonces evala la expr_si_cumple. Si condicin devuelve nil evala la expr_no_cumple, si existe y si no existe no hace nada. Veamos algunos ejemplos: (if (= 2 2.0) (alert "Los nmeros son iguales")) La condicin a evaluar es: (= 2 2.0) en la que tenemos un nmero 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 condicin devuelve T, evaluandose la condicin si cumple que muestra un mensaje de alerta. La funcin IF devuelve el valor de la ltima expresin evaluada, es decir alert, que es nil. (if (= 2 3) (alert "Los nmeros son iguales")) En este caso la condicin devuelve nil y al no existir expresin no cumple, no hara nada ms. Qu valor devolvera IF? El de siempre, el valor de la ltima expresin evaluada, que en este caso ha sido la propia condicin que devuelve nil. De modo que IF devolver nil. (if (= 2 3) (alert "Los nmeros son iguales") (alert "Los nmeros son distintos") ) En este caso el cdigo se ha escrito en varias lneas y tabulado para facilitar su comprensin. Ahora si tenemos una expresin no cumple, que ser evaluada ya que la condicin devuelve nil. Veamos el siguiente ejemplo: (setq liminf (getint "\nLmite inferior")) (setq limsup (getint "\nLmite superior"))

(if (> liminf limsup) (alert "El lmite superior debe ser mayor que el inferior") (setq limsup (getint "\nLmite superior")) (setq intervalo (- limsup liminf)) (setq intervalo (- limsup liminf)) ) Viendo el cdigo anterior, tal vez pienses que si la condicin (> liminf limsup) se cumple, entonces se evaluar la lnea siguiente de cdigo. Pero no es as, se evala la expresin si cumple, que es la primera expresin (alert "El lmite superior debe ser mayor que el inferior"). Si la condicin no se cumple, devuelve nil, se evaluar la expresin no cumple, que en este caso ser (setq limsup (getint "\nLmite superior")). Tanto la expresin si cumple, como la no cumple solo pueden ser una nica expresin. El ejemplo de cdigo anterior nos dara un error ya que IF no puede tener ms que 3 expresiones:

La condicin La expresin si cumple La expresin no cumple

(PROGN expr1 expr2 ...) Para que se pueda indicar ms de una expresin si cumple, o no cumple, en la funcin IF se suele emplear la funcin PROGN. El valor devuelto por PROGN es el de la ltima expresin que recibe como argumento. Esta funcin en realidad no hace nada, tan solo nos permite agrupar una serie de expresiones. Cmo quedara el ejemplo anterior? (setq liminf (getint "\nLmite inferior")) (setq limsup (getint "\nLmite superior")) (if (> liminf limsup) (progn (alert "El lmite superior debe ser mayor que el inferior") (setq limsup (getint "\nLmite superior")) (setq intervalo (- limsup liminf)) ) (setq intervalo (- limsup liminf)) ) En este caso la condicin si cumple es todo lo siguiente: (progn (alert "El lmite superior debe ser mayor que el inferior") (setq limsup (getint "\nLmite superior"))

(setq intervalo (- limsup liminf)) ) Si se cumple la condicin, se evala la condicin si cumple, es decir el progn. De modo que se van evaluando las expresiones contenidas en la funcin PROGN, que devuelve el valor de la ltima expresin (setq intervalo (- limsup liminf)), que ser el valor de la variable intervalo. En caso de que la condicin no se cumpla, se evala la condicin no cumple, (setq intervalo (- limsup liminf)) que curiosamente tambin devuelve el valor de la variable intervalo. Pasemos ahora a unos ejemplos ms de estructuras condicionales: (if (and (= 2 2.0) (< 2 3)) (alert "Las dos condiciones se cumplen") (alert "Al menos una condicin no se cumple") ) En este caso la condicin es (and (= 2 2.0) (< 2 3)) que en este caso devolvera T, ya que se verifican las dos expresiones que recibe la funcin AND. (if (not var1) (alert "Variable no definida") (alert "Variable definida") ) En este caso si var1 es nil, (not var1) devolver T, indicando que la variable no se ha definido. En caso contrario, (not var1) devolver nil evaluandose la expresin no cumple. Otro mtodo para hacer lo mismo, sera: (if var1 (alert "Variable definida") (alert "Variable no definida") ) Si var1 es distinto de nil, se evala la expresin si cumple. En caso de que var1 sea nil, se evaluara la expresin no cumple. Estructuras condicionales mltiples Con la funcin IF tan solo podemos indicar una condicin y dos opciones, si cumple y no cumple. Pero tambin tenemos la posibilidad de indicar varias condiciones con sus respectivas expresiones si cumple. (COND (condicion1 [expr1_1] [expr1_2] ... ) [ (condicion2 [expr2_1] [expr2_2] ... ) ] ... ) Esta funcin es similar a IF en cuanto a que se indican condiciones y una serie de expresiones que se evaluaran si se verifica cada condicin. n este caso no existe la limitacin de indicar tan slo una nica expresin si cumple, se pueden indicar tantas como se desee. COND evaluar la primera condicin

encontrada, si se verifica evaluar las expresiones si cumple de dicha condicin. En caso de que no se verifique, evaluar la siguiente expresin. Por lo tanto, COND evaluar las expresiones si cumple de la primera condicin que se verifique. Y devolver el resultado de evaluar la ltima expresin si cumple. Pongamos un ejemplo: (cond ((= a b) (alert "A y B son iguales") (setq b (getreal "Introduzca de nuevo B: ")) ) ((< a c) (alert "A es menor que C") ) ((< a b)) (T (alert "A no es igual a B") (alert "A no es menor que C") (alert "A no es menor que B") ) ) Supongamos ahora que antes de la expresin COND, hemos definido (setq a 2 b 2.0 c 3.5). En este caso, al entrar en COND se evaluar la primera condicin (= a b) que se verifica, de modo que se evaluaran las expresiones si cumple de esta condicin: (alert "A y B son iguales") y (setq b (getreal "Introduzca de nuevo B: ")) finalizando la expresin COND que devuelve el nuevo valor de b. Aunque la siguiente condicin (< a c) se cumple, no se evala, ya que existe una condicin anterior que tambin se cumpla. En caso de que se hubiera definido (setq a 2 b 2.5 c 3.5) la primera condicin (= a b) no se verifica, de modo que se evala la segunda condicin (< a c) que si se cumple. Evalundose sus expresiones si cumple (alert "A es menor que C") que devuelve nil. Si fuera (setq a 2 b 2.5 c 1.5) la primera condicin en cumplirse sera la tercera (< a b) que no tiene expresiones si cumple. Qu devolver entonces la funcin COND? Pues el valor de la ltima expresin evaluada, es decir el valor devuelto por la condivin (< a b) que es T. Por ltimo, si tenemos (setq a 2 b 1.0 c 1.5) ninguna de las tres condiciones anteriores se verifica, de modo que pasamos a la siguiente condicin T, que lgicamente siempre devuelve T, as que siempre se verifica. Esto se suele utilizar mucho en la funcin COND, aadir como ltima condicin una que se verifique siempre. En lugar de T se poda haber puesto (not nil) o (= 1 1.0) que tambin son expresiones que siempre se cumplen. Para qu aadir una expresin que siempre se cumple? Muy sencillo, para incluir el cdigo que se desea ejecutar en caso de que no se verifique ninguna de las condiciones anteriores.

Y qu sucede si no se pone la condicin T como ltima condicin? Pues sucede que las que estn a continuacin nunca se evaluarn, ya que T siempre se cumplir. Si en el ejemplo anterior hubieramos puesto: (cond (T (alert "A no es igual a B") (alert "A no es menor que C") (alert "A no es menor que B") ) ((= a b) (alert "A y B son iguales") (setq b (getreal "Introduzca de nuevo B: ")) ) ((< a c) (alert "A es menor que C") ) ((< a b)) ) Independientemente de los valores de las variables a b y c siempre nos dira que A no es igual a B, A no es menor que C y A no es menor que B. Qu es una variable de sistema? El modo en que funciona el entorno de AutoCAD, y la forma de trabajar de muchos de sus comandos, se ven afectados por los valores asignados a las variables de sistema. Podra decirse que controlan como trabaja AutoCAD. Por lo tanto, se pueden modificar los valores asignados a las variables de sistema para personalizar AutoCAD para un usuario en concreto, para un tipo de trabajo determinado, e incluso para un archivo de dibujo. De modo que nos facilite el trabajar con AutoCAD. Tipos de variables de sistema Las variables de sistema pueden almacenar distintos tipos de datos, en funcin de la informacin que contengan. Podran clasificarse del siguiente modo:

Activada / Desactivada. Muchas variables de sistema slo admiten dos opciones: Activada y Desactivada. Normalmente tienen asignado el valor "0" cuando estn desactivadas, y "1" cuando estn activadas. Un ejemplo de este tipo de variables es "blipmode". Nmeros enteros. Otras variables tienen ms de dos posibilidades, para lo que asignan un nmero entero para cada opcin. Normalmente emplean nmeros correlativos, empezando desde el cero. Una variable que utiliza este tipo de datos es "coords". Cdigos binarios. Algunas variables pueden emplear varias opciones a la vez, para lo que suelen emplear cdigos binarios. A cada opcin se le asigna el nmero resultante de elevar 2 a n.

Asignando a n nmeros entero correlativos a partir del cero. Es decir, los valores para las distintas opciones sern: 1,2,4,8,16,32,etc. De modo que para seleccionar la primera y cuarta opciones, hay que asignar a la variable la suma de sus valores: 1+8 = 9. Un ejemplo muy interesante de este tipo de variables es "osmode".

Nmeros reales. Las variables que almacenan valores de ngulos o distancias, por ejemplo, utilizan este tipo de valores. Un ejemplo de este tipo es la variable "chamfera". Puntos. Este tipo de entidades almacenan las coordenadas de un punto, un buen ejemplo es "ucsorg". Cadenas de texto. Hay bastantes variables que almacenan cadenas de texto, como nombres de archivos o rutas de directorios. Ejemplos de este tipo de variables son "acadver" y "acadprefix".

Modificar los valores de las variables de sistema Podramos clasificar las variables de sistema en funcin del lugar en el que se guardan:

No guardadas. La informacin asignada a este tipo de entidades no se guarda. Un ejemplo de este tipo de variables es "acadver". En el dibujo. La mayora de las variables de sistema son de este tipo, de modo que cada dibujo trabajar con unos valores determinados para las variables de sistema. Esto hace sumamente importante la definicin de los valores de las variables de sistema en las plantillas utilizadas para crear nuevos dibujos. Un ejemplo de variable guardada en el dibujo es "luprec". En el registro. Algunas variables de sistema se guardan en el registro de Windows. Por ejemplo "attdia".

La mayora de las variables de sistema de AutoCAD pueden editarse, modificando el valor que tengan asignado. Pero algunas variables son de solo lectura, de modo que no se pueden modificar, tan solo leer. Un ejemplo de variable de solo lectura es "cdate". Hay varios mtodos para modificar los valores asignados a las variables de sistema de AutoCAD:

Tecleando el nombre de la variable, como si fuese un comando ms de AutoCAD. Ejecutando el comando "MODIVAR" e indicando la variable a modificar. Algunos comandos de AutoCAD permiten modificar los valores asignados a algunas variables de sistema. Por ejemplo el comando "MARCAAUX" permite modificar el valor asignado a la variable "blipmode". Algunas variables se modifican al ejecutar algn comando de AutoCAD. Por ejemplo "circlerad" almacena el radio de la ltima circunferencia creada con el comando "crculo".

Las variables de sistema en los programas de AutoLISP Los programadores suelen leer y modificar el contenido de algunas variables de sistema de AutoCAD, ya que esto les permite definir el modo en el que desean que se comporte AutoCAD o algunos comandos de AutoCAD. Esto puede ocasionar cambios en los valores asignados a algunas de las variables de sistema. Al programar deberas seguir los siguientes consejos para que esto no suceda: Deberas guardar los valores iniciales de las variables de sistema que se necesite modificar, y asignarles sus valores iniciales al terminar el programa. Crear una funcin de tratamiento de errores, de modo que si se produce algn error al ejecutar el programa se restablezcan los valores iniciales de las variables de sistema. La creacin de funciones de tratamiento de errores la trataremos ms adelante. (GETVAR variable) Esta funcin devuelve el valor asociado a la variable que recibe como argumento. Por ejemplo: (getvar "osmode")

(getvar "blipmode") (getvar "acadver") (SETVAR variable nuevo_valor) Asigna a la vaiable indicada un nuevo valor, y devuelve dicho valor. (setvar "blipmode" 0) (setvar "osmode" 32) Veamos un ejemplo combinando las funciones GETVAR y SETVAR. Escribe lo siguiente en la lnea de comandos de AutoCAD: (getvar "luprec") (setvar "luprec" 2) (getvar "luprec") Funciones de conversin de datos Vamos a ver una serie de funciones que nos permitirn pasar un entero a real, a un texto o al revs... (ITOA entero) Convierte un entero en un texto. Integer TO Atom. (itoa 24) devuelve "24" (ATOI texto) Hace justo lo contrario que la funcin anterior. Convierte un texto en un nmero. Atom TO Integer (atoi "24") devuelve 24 (atoi "-7") devuelve -7 Y que pasa si hacemos... (atoi "soy un texto") pues que devuelve cero. Siempre que escribes algo que no sea un nmero devuelve 0. (atoi "15.3") devuelve 15 (atoi "15.99999") devuelve 15 (FLOAT numero) Convierte un nmero en real, as que lo lgico es pasarle un entero. (float 5) devuelve 5.0 pero tb puedo hacer... (float 5.36) devuelve 5.36 lo cual sera una tontera porque no hace nada. (ATOF texto)

Convierte un texto en real. (atof "15.7") devuelve 15.7 (atof "15") devuelve 15.0 (atof "-15.7") devuelve -15.7 (atof "soy un texto") devuelve 0.0 (RTOS numero [modo [precision]]) La ltima funcin de este tipo que vamos a ver es algo ms complicada, pero no mucho. Convierte un real en texto. Real TO String. (rtos 2.5) devuelve "2.5" (rtos 2) devuelve "2" y no "2.0" Veamos para qu sirven los argumentos opcionales [modo [precisin]]. Modo, permite indicar un tipo de expresar los nmeros, es decir, permite seleccionar el formato utilizado para los nmeros. Hay 5 opciones: 1. 2. 3. 4. 5. Formato cientfico. Decimal. Es el que se usa habitualmente. Pies y pulgadas. Pies y pulgadas en fracciones. Fracciones.

Por ejemplo... (rtos 2.5 5) devuelve "2 1/2" (rtos 2.5 1) devuelve "2.5000E+00" Precisin nos permite definir el nmero de decimales que deseamos.. por ejemplo: (rtos 1.23456789 2 3) devuelve 1.235 as que redondea... (rtos 9.99 2 0) devuelve 10 Obtener distancias y ngulos del usuario Hemos visto como se obtienen nmeros, puntos y textos del usuario. Ahora le vamos a solicitar directamente una distancia o un ngulo. (GETDIST [pto_base] [mensaje]) Solicita una distancia y devuelve esa distancia como un nmero real. El usuario podr indicar la distancia por medio de un nmero o indicando 2 ptos en pantalla. (getdist "Distancia de desplazamiento:") El mensaje es opcional, pero casi siempre se pone. Tambin podemos asignar el resultado a una variable... (setq dist1 (getdist "Distancia de desplazamiento:")) En muchas ocasiones se puede reemplazar a la funcin GETREAL por GETDIST si lo que se pide se puede relacionar con alguna distancia del dibujo. Por ejemplo, en nuestra rutina CIRCPERI podramos dibujar una circunferencia de permetro la longitud de una recta.

El argumento opcional [pto_base] funciona de modo similar a como lo hace en GETPOINT permitiendo indicar la distancia a partir de un punto de origen ya predefinido: (setq pto (getpoint "Punto base")) (setq dist1 (getdist pto "Distancia de desplazamiento:")) (GETANGLE [pto_base] [mensaje]) Solicita un ngulo al usuario que puede escribirlo, o designarlo por medio de dos puntos. Devuelve el ngulo en radianes aunque el usuario lo escribira en las unidades actuales (generalmente grados decimales). Por ejemplo, a la expresin: (getangle "Angulo: ") el usuario responde... 180.0 y getangle, devuelve pi. Toma como origen de ngulos el actual, que suele coincidir con el sentido positivo del eje X del SCU (Sistema de Coordenadas Universal). Pero siempre considerar como sentido positivo de los ngulos el antihorario. El parmetro opcional [pto_base] permite indicar un punto a partir del cual indicar el ngulo. (GETORIENT [pto_base] [mensaje]) Es casi igual a GETANGLE, y tambin se utiliza para solicitar un ngulo al usuario. La nica diferencia es que el origen de ngulos siempre es el del sentido positivo del eje X del SCU (Sistema de Coordenadas Universal). Vamos ahora a modificar un poco la rutina CIRCPERI. Tenamos el siguiente cdigo: (defun C:CIRCPERI ( / pto peri rad ) (setq pto (getpoint "\nCentro de la circunferencia: ")) (setq peri (getreal "\nPermetro: ")) (setq rad (/ peri (* pi 2))) (command "_.circle" pto rad) ) (prompt "\nNuevo comando CIRCPERI cargado") Si sustituimos la funcin getreal por getdist adems de poder escribir un permetro directamente, tambin podremos indicarlo mediante dos puntos . Por otra parte, Si habeis ejecutado el comando CIRCPERI vereis que lo de los mensajes queda mejor que al principio pero tampoco es perfecto ya que aparecen unos textos en la ventana de comandos que no se sabe de dnde salen: Centro de la circunferencia: Permetro: Designe segundo punto: _.circle Precise punto central para crculo o [3P/2P/Ttr (tangente tangente radio)]: Precise radio de crculo o [Dimetro]: 23.00636765228087 Esto queda bastante feo y como hay que ser profesionales, vamos a ponerlo bien. Hemos visto las funciones GETVAR y SETVAR que nos permiten acceder a las variables de sistema de AutoCAD. Pues hay una variable que controla el "eco de mensajes", es decir, el que aparezcan esos mensajes en pantalla. La variable se llama CMDECHO y solo admite dos valores posibles:

0 Desactivada 1 Activada

Por defecto debera estar activada (1) as que se veran los mensajes raros de antes. Cmo evitamos que aparezcan estos mensajes? Pues desactivando la variable. Pero no conviene modificar los valores de las variables de sistema, porque tal vez el usuario los quiera mentener como estaban. De modo que se desactivar momentaneamente y al terminar la rutina se dejar con su valor inicial, es decir tal como estaba. Esto es ms bien una filosofa de vida: "Si al entrar en un sitio, la puerta estaba cerrada, vuelve a cerrarla" As que nuestro cdigo quedara... (defun C:CIRCPERI ( / pto peri rad ) (setvar "cmdecho" 0) ; Desactiva el eco de mensajes (setq pto (getpoint "\nCentro de la circunferencia: ")) (setq peri (getdist "\nPermetro: ")) (setq rad (/ peri (* pi 2))) (command "_.circle" pto rad) (setvar "cmdecho" 1) ; Vuelve a activar el eco de mensajes ) (prompt "\nNuevo comando CIRCPERI cargado") Efectivamente los mensajes raros desaparecen, pero... Qu pasa si al entrar en una habitacin, la puerta ya estaba abierta? la cerramos o la dejamos abierta de nuevo?. Lo mejor es que todo quede "tal como lo encontramos". As nadie nos dir... "Por qu cerraste la puerta? No ves que aqui no hay quien respire...". Y si lo dice, le respondes: "Perdona, pero mi rutina, deja las cosas tal y como estaban, as que o estaba cerrada antes o la cerraron despus". Si cmdecho est inicialmente desactivada nuestra rutina la descativa, o lo intenta, y luego la activa. Quedando por lo tanto cmdecho activada. As que vamos a modificar el cdigo para que cmdecho quede con el valor que tena antes de ejecutar la rutina... Lo primero que tenemos que saber es si la puerta est cerrada, si est cerrada la abrimos y si ya est abierta no hacemos nada, simplemente pasamos. (getvar "cmdecho") me dir si cmdecho est activada o desactivada. Veamos que es lo que hay que hacer: 1. 2. 3. Si la puerta est cerrada, la abro. Es decir, si cmdecho est activada, la desactivo. Aqui metemos el cdigo de la rutina CIRCPERI Si antes de entrar la puerta estaba cerrada , entonces la cierro. Es decir, si cmdecho antes de entrar estaba activada, entonces la activo.

El apartado 1 suena claramente a una estructura condicional, as que emplearemos la funcin IF: (if (= (getvar "cmdecho") 1) (setvar "cmdecho" 0)

) Y cmo haramos al final de la rutina en el apartado 3? Sera otro condicional, pero necesitamos conocer el valor que tena inicialmente la variable cmdecho para saber si estaba activada o desactivada. De modo que hay que modificar el cdigo anterior para que en el apartado 1 se almacene el valor de cmdecho. (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) De este modo la variable cmd0 almacena el valor inicial de cmdecho. Es bastante habitual almacenar los valores iniciales en variables cuyo nombre sea del tipo cmd0, osm0 o blip0 pues el 0 nos indica que almacena el valor inicial. Ahora ya podemos poner el cdigo del apartado 3. Podramos hacerlo de dos formas: (setvar "cmdecho" cmd0) Esta lnea de cdigo asignara a cmdecho el valor que tena inicialmente. (if (= cmd0 1) (setvar "cmdecho" 1) ) En este caso, si cmdecho estba inicialmente activada entonces la activa. Si no estaba inicialmente activada, es decir estaba desactivada, entonces no hace nada porque cmdecho ya est desactivada, al igual que al principio. El cdigo quedara as: (defun C:CIRCPERI ( / pto peri rad cmd0 ) (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) (setq pto (getpoint "\nCentro de la circunferencia: ")) (setq peri (getdist "\nPermetro: ")) (setq rad (/ peri (* pi 2))) (command "_.circle" pto rad) (if (= cmd0 1) (setvar "cmdecho" 1) ) )

(prompt "\nNuevo comando CIRCPERI cargado") An nos queda una cosa... al ejecutar ahora la rutina muestra el siguiente texto: Centro de la circunferencia Permetro: Designe segundo punto: 1 Aparece un 1 al final del texto. Ese 1 es el resultado de la evaluacin de la ltima expresin de CIRCPERI (setvar "cmdecho" 1). Para que la salida de nuestras rutinas sea "limpia" aadiremos al final del cdigo de esta una funcin que devuelva nil, as no escribir nada. Por ejemplo, se podra aadir (prompt "") aunque suele emplearse la funcin (princ) que an no hemos visto, pero que tambin devuelve nil. Finalmente el cdigo resultante ser: (defun C:CIRCPERI ( / pto peri rad cmd0 ) (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) (setq pto (getpoint "\nCentro de la circunferencia: ")) (setq peri (getdist "\nPermetro: ")) (setq rad (/ peri (* pi 2))) (command "_.circle" pto rad) (if (= cmd0 1) (setvar "cmdecho" 1) ) (princ) ) (prompt "\nNuevo comando CIRCPERI cargado") En este tema no vamos a ver ninguna funcin de AutoLISP. Vamos a crear un nuevo comando y a repasar un poco el comando CIRCPERI. Comenzaremos creando un nuevo comando... Crear un comando para dibujar arandelas en 2D. El programa solicitar al usuario el centro de la arandela, el dimetro interior y el exterior. Se dibujarn dos circunferencias concncricas con los dimetros indicados. Y antes de ponerse a escribir cdigo hay que ... escribir el pseudocdigo. Veamos, podamos hacer algo as: 1. 2. 3. 4. 5. Obtener el centro del cculo Obtener el radio del crculo interior Dibujar el circulo interior Obtener el radio del crculo exterior Dibujar el circulo exterior

AutoCAD ya tiene un comando que se llama arandela (en ingls donuts) as que buscaremos otro nombre para nuestra rutina, por ejemplo ARAND. Es mejor utilizar nombres ms bien cortos y que evoquen a la funcin que tiene el comando. La primera lnea de cdigo es la definicin de la funcin: (defun C:ARAND ( ) Ms adelante le aadirmos las varibles locales si es que existen. 1) Obtener el centro del cculo. Podra ser algo as: (setq pto (getpoint "\nCentro de la arandela: ")) 2) Obtener el radio del crculo interior: (setq radi (getreal "\nRadio interior: ")) 3) Dibujar el circulo interior (command "_.circle" pto radi) 4) Obtener el radio del crculo exterior: (setq rade (getreal "\nRadio exterior: ")) 5) Dibujar el circulo exterior (command "_.circle" pto rade) Y por ltimo cerramos el parntesis de la funcin defun ) El cdigo completo queda as: (defun C:ARAND ( ) (setq pto (getpoint "\nCentro de la arandela: ")) (setq radi (getreal "\nRadio interior: ")) (command "_.circle" pto radi) (setq rade (getreal "\nRadio exterior: ")) (command "_.circle" pto rade) ) Ahora vamos a retocar un poco el comando, le aadiremos las siguientes mejoras:

Desactivar el eco de mensajes, es decir desactivar la variable cmdecho al iniciar la rutina. Recuperar el valor inicial de cmdecho al terminar la rutina. Aadir una lnea al final del cdigo para que muestre un mensaje indicando el nombre del nuevo comando al cargar la funcin.

Poner las variables como locales. En lugar de utilizar GETREAL para obtener el radio, usaremos GETDIST con el centro de las circunferencias como punto base. Aadir una lnea al final de la funcin para que la salida del programa sea limpia.

El cdigo despus de realizar las mejoras anteriores sera: (defun C:ARAND ( / pto rad cmd0 ) (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) (setq pto (getpoint "\nCentro de la arandela: ")) (setq rad (getreal "\nRadio interior: ")) (command "_.circle" pto rad) (setq rad (getreal "\nRadio exterior: ")) (command "_.circle" pto rad) (if (= cmd0 1) (setvar "cmdecho" 1) ) (princ) ) (prompt "\nNuevo comando ARAND cargado") Adems de las mejoras indicadas anteriormente, ahora la variable rad se utiliza tanto para almacenar el radio de la circunferencia interior como el de la exterior. Puede dar lugar esto a algn problema? Veamoslo: En primer lugar obtenemos el centro de la circunferencia y la asignamos a la variable pto. (setq pto (getpoint "\nCentro de la arandela: ")) Solicitamos el radio de la circunferencia interior y lo asignamos a la variable rad. (setq rad (getreal "\nRadio interior: ")) y dibujamos la circunferencia interior (command "_.circle" pto rad) El radio de la circunferencia interior no lo vamos a volver a utilizar en nuestra rutina, de modo que podemos reutilizar esa variable y asignarle otro valor, el radio de la circunferemcia exterior. (setq rad (getreal "\nRadio exterior: "))

Ahora la variable rad almacena el radio de la circunferencia exterior y pto el centro de las dos circunferencias, de modo que dibujamos la circunferencia exterior. (command "_.circle" pto rad) De este modo nos ahorramos una variable. Si es posible conviene reutilizar las variables. Si cargamos la rutina y ejecutamos el comando ARAND dibujaremos una arandela formada por dos crculos. Pero qu pasa si despus de dibujarla ejecutamos el comando "H" (deshacer)? Pues que "deshace el ltimo comando ejecutado" que no es ARAND sino (command "_.circle" pto rad) de modo que deshace el crculo exterior. Pero el interior no. Control de deshacer comandos El comando "DESHACER" es distinto al comando "H". En realidad, "H" es una opcin de "DESHACER". Al ejecutar este comando, AutoCAD muestra el siguiente mensaje: "Indique el nmero de operaciones a deshacer o [Auto/Control/Inicio/Fin/Marca/Retorno] <1>:". Veamos como funcionan estas opciones: "Nmero de operaciones a deshacer" La opcin por defecto es indicar el nmero de operaciones a deshacer, que por defecto tiene el valor 1. Esta opcin funciona exactamente igual que el comando "H". Pero, podemos indicarle un nmero de operaciones superior a 1 (cualquier nmero entero entre 1 y 32767). Esta opcin es til para deshacer los cambios realizados por los ltimos "n" comandos. "Control" Al seleccionar "Control" nos ofrece las siguientes posibilidades: "Indique una opcin de control DESHACER [Todas/Ninguna/Una] <Todas>:"

Con la opcin "Todas" seleccionada (es la opcin por defecto), AutoCAD almacena en el archivo temporal "UNDO.ac$" la informacin sobre los comandos ejecutados en el dibujo actual y por tanto que se pueden deshacer. Tambin almacena en el archivo temporal "REDO.ac$" la informacin sobre los comandos del dibujo actual que se han deshecho. Estos archivos se almacenan en el directorio temporal del sistema operativo. Esta opcin permite deshacer todos los comandos realizados en el dibujo durante la sesin actual. Si se selecciona la opcin "Una", tan slo se podr deshacer el ltimo comando ejecutado. Quedando desactivadas todas las opciones del comando "DESHACER" excepto "Control" e "Indique el nmero de operaciones a deshacer <1>:" Seleccionando la opcin "Ninguna" se elimina la capacidad de revocar de los comandos "H" y "DESHACER", quedando estos desactivados. Y ya no se utilizan los archivos temporales anteriormente citados.

Lo recomendable es tener siempre activada la opcin "Todas". "Marca y Retorno" Estas dos opciones funcionan en pareja. Supongamos que vamos a ejecutar una serie de comandos en el dibujo actual, pero no sabemos si el resultado obtenido ser el deseado. En este caso, antes de comenzar puedes ejecutar el comando "DESHACER" y seleccionar la opcin "Marca". De este modo activas una marca, a la que podrs volver en cualquier momento ejecutando "DESHACER" con la opcin "Retorno". Al encontrar una marca AutoCAD mostrar el mensaje "Marca encontrada". Si en lugar de volver a la marca lo que quieres es deshacer un nmero determinado de comandos, puedes ejecutar el comando "H" o "DESHACER" indicando el nmero de comandos a deshacer. Adems, puedes definir tantas marcas como desees, y cada vez que ejecutes "DESHACER" "Retorno" volvers a la marca anterior. Si no existen ms marcas, o si no se ha definido ninguna marca, AutoCAD preguntar si se desea deshacer todo.

"Auto" Algunos comandos de AutoCAD, estn formadas por un grupo de rdenes. De modo que el comando "H" no anulara todo el grupo de comandos ejecutados, sino slo el ltimo. Activando esta opcin se agrupan estos comandos en uno slo, a efectos de la aplicacin de los comandos "H" y "DESHACER". "Inicio y Fin" Estas dos opciones tambin funcionan juntas. Con ellas podemos agrupar una serie de comandos, de modo que sean tratados como uno solo al ejecutar "H" o "DESHACER". Si "DESHACER" "Auto" est activada, AutoCAD coloca automticamente un "DESHACER" "Inicio" y un "DESHACER" "Fin" al principio y final de las opciones de los mens, y deja inutilizadas estas opciones de forma manual. Con la opcin "Auto" desactivada, las opciones "Inicio" y "Fin" se ejecutan de forma anloga a como se hace con "Marca" y "Retorno". Si se vuelve a ejecutar la opcin "Inicio" sin haber ejecutado la opcin "Fin" para cerrar un grupo anterior, AutoCAD entiende que se quiere cerrar el grupo anterior y abrir uno nuevo. El comando deshacer en las rutinas de AutoLISP Utilizando las opciones anteriores del comando deshacer, se puede lograr que todo el cdigo de nuestras rutinas funcione como si se tratase de un nico comando, aunque en realidad se utilice ms de una llamada a la funcin command de AutoLISP. Aadiremos al inicio de nuestra rutina "deshacer" "inicio" y al final de la rutina "deshacer" "fin". Veamoslo: (defun C:ARAND ( / pto rad cmd0 ) (command "_.undo" "_begin") (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) (setq pto (getpoint "\nCentro de la arandela: ")) (setq rad (getreal "\nRadio interior: ")) (command "_.circle" pto rad) (setq rad (getreal "\nRadio exterior: ")) (command "_.circle" pto rad) (if (= cmd0 1) (setvar "cmdecho" 1) ) (command "_undo" "_end") (princ)

) (prompt "\nNuevo comando ARAND cargado") Deberamos aadir estas dos lneas en la rutina CIRCPERI? Pues no es necesario, puesto que en CIRCPERI tan solo utilizamos un comando. As que el comando de AutoCAD "H" deshara ese comando. Funciones de tratamiento de errores Las rutinas que hemos creado (CIRCPERI y ARAND) no est terminadas todava. Para terminarlas debemos realizar dos modificaciones: 1. 2. Controlar lo que hace la rutina en caso de que se produzca un error en su ejecucin. Impedir que el usuario introduzca datos erroreos. Por ejemplo que indique 0 como radio de un crculo.

En este tema se estudiar el primero de los dos apartados anteriores, y en el siguiente tema veremos el segundo apartado. Fijmonos en el cdigo de la rutina ARAND: (defun C:ARAND ( / pto rad cmd0 ) (command "_.undo" "_begin") (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) (setq pto (getpoint "\nCentro de la arandela: ")) (setq rad (getreal "\nRadio interior: ")) (command "_.circle" pto rad) (setq rad (getreal "\nRadio exterior: ")) (command "_.circle" pto rad) (if (= cmd0 1) (setvar "cmdecho" 1) ) (command "_.undo" "_end") (princ) ) (prompt "\nNuevo comando ARAND cargado") Qu sucede si el usuario como respuesta a la peticin del radio interior pulsa Intro? Pues que se asigna a la variable rad el valor nil. Produciendose un error al evaluar la siguiente lnea de cdigo: (command "_.circle" pto rad)

En el siguiente tema veremos cmo se puede evitar que el usuario introduzca datos erroneos. Por ejemplo, evitar que como respuesta a la peticin del radio interior se introduzca Intro. Pero una cosa est clara, no siempre vamos a poder controlar todos los posibles errores en nuestras rutinas. De modo que necesitamos una funcin de tratamiento de errores que informe al usuario del tipo de error que se produce. Por ejemplo, indicarle al usuario que ha introducido un dato incorrecto. AutoCAD en este caso nos dice: "; error: tipo de argumento errneo: numberp: nil" AutoLISP dispone de una funcin de tratamiento de errores por defect. Dicha funcin se llama *error* y recibe como argumento una cadena de texto con la descripcin del error que se ha producido. Podemos redefinir la funcin de tratamiento de errores que ofrece AutoLISP, personalizndola a nuestro gusto en funcin de nuestros intereses. Vamos a crear una funcin de tratamiento de errores a la que llamaremos ERRORES: (defun ERRORES ( mens ) (if (= mens "quitar / salir abandonar") (princ) (princ (strcat "\nError: " mens " ")) ) (princ) ) No nos paremos demasiado aqui, ya lo veremos bien ms adelante. Hemos creado una funcin llamada ERRORES para el tratamiento de errores en nuestras rutinas. Ahora habr que decirle al comando ARAND que utilice esta funcin de tratamiento de errores. Antes, conviene explicar cmo se pueden redefinir las funciones de AutoLISP. Hay mtodos: 1. 2. Crear una funcin con el mismo nombre. Por ejemplo, (defun SIN ... Esto redefinira la funcin SIN de AutoLISP. Asignarle un valor distinto mediante setq. Por ejemplo, si hacemos (setq sin cos) la funcin SIN de AutoLISP pasar a funcionar como la funcin COS, devolviendo el coseno de un ngulo en lugar de el seno. (sin 0.0) ahora devolvera 1.0 en lugar de 0.

Veamos ahora el cdigo de la funcin ARAND: (defun C:ARAND ( / pto rad cmd0 error0 ) (command "_.undo" "_begin") (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) (setq error0 *error* *error* errores) (setq pto (getpoint "\nCentro de la arandela: ")) (setq rad (getreal "\nRadio interior: "))

(command "_.circle" pto rad) (setq rad (getreal "\nRadio exterior: ")) (command "_.circle" pto rad) (setq *error* error0) (if (= cmd0 1) (setvar "cmdecho" 1) ) (command "_.undo" "_end") (princ) ) (prompt "\nNuevo comando ARAND cargado") En la lnea (setq error0 *error* *error* errores) se asigna a la variable error0 la antigua funcin de tratamiento de errores, *error*. Y se define la funcin de tratamiento de errores como nuestra funcin ERRORES. Por supuesto, le decimos que use nuestra funcin de tratamiento de errores al principio del cdigo. Pero siempre despus de desactivar el eco de mensajes. Hemos redefinido la funcin de tratamiento de errores, pero al terminar la rutina debemos ponerlo todo como estaba antes. As que recuperamos la funcin de tratamiento de errores inicial, la que ofrece AutoLISP por defecto. Lo haramos con la siguiente lnea: (setq *error* erro0) que se debe poner antes de volver a activar el eco de mensajes. En la rutina CIRCPERI hariamos exactamente lo mismo: (defun C:CIRCPERI ( / pto peri rad cmd0 error0 ) (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) (setq error0 *error* *error* errores) (setq pto (getpoint "\nCentro de la circunferencia: ")) (setq peri (getdist "\nPermetro: ")) (setq rad (/ peri (* pi 2))) (command "_.circle" pto rad) (setq *error* error0) (if (= cmd0 1) (setvar "cmdecho" 1)

) (princ) ) (prompt "\nNuevo comando CIRCPERI cargado") Repasemos un poco el funcionamiento de la rutina CIRCPERI: Lo primero que hacemos es desactivar el eco de mensajes, a continuacin redefinimos la funcin de tratamiento de errores, despus viene el cdigo de la funcin, se restituye la funcin de tratamiento de errores de AutoLISP y se recupera el valor inicial de "cmdecho" (que controla el eco de mensajes). Qu diferencia existe entre nuestra funcin de tratamiento de errores y la ofrecida por defecto por AutoLISP? Casi ninguna, de momento tan solo se diferencia en que si el error que se ha producido tiene por descripcin "quitar / salir abandonar", que equivale a pulsar la tecla de ESCape no hace nada. Si el error no se ha producido por pulsar ESC, sino por otra causa, entonces mostrar un mensaje indicando: "Error: Descripcin del error". Un "gran" cambio, no? Bueno los cambios vamos a hacerlos ahora que ya sabemos como funciona la funcin ERRORES. Qu sucede si el usuario introduce un dato incorrecto al solicitarle el permetro? En ese caso, en la siguiente lnea (setq rad (/ peri (* pi 2))) se producira un error, iniciandose la funcin de tratamiento de errores. Cuando se produce el error, ya se ha evaluado la lnea (setq error0 *error* *error* errores) en CIRCPERI, de modo que hemos redefinido la funcin de tratamiento de errores. As que se ejecuta la funcin ERRORES, que muestra la descripcin del error que se produjo. Pero, salimos de la funcin ERRORES y no hemos recuperado el valor de la funcin de tratamiento de errores que nos ofrece por defecto AutoLISP. Y tambin se ha desactivado el eco de mensajes, pero no recuperamos su valor inicial. De modo que vamos a modificar la funcin ERRORES: (defun ERRORES ( mens ) (setq *error* error0) (if (= mens "quitar / salir abandonar") (princ) (princ (strcat "\nError: " mens " ")) ) (if (= cmd0 1) (setvar "cmdecho" 1) ) (princ) ) Ahora nuestra funcin ERRORES restituye el valor inicial del eco de mensajes y la funcin de tratamiento de errores inicial. Puede extraar que se utilicen las variables cmd0 y error0 dentro de la funcin ERRORES, ya que estaban definidas como variables locales en CIRCPERI. Cuando definimos una variable local en una funcin, esta variable se puede utilizar slo dentro de esa funcin. Pero, si desde CIRCPERI "llamamos" a otra funcin, por ejemplo ERRORES, en realidad estamos dentro de ERRORES, que est dentro de CIRCPERI. As que seguimos dentro de CIRCPERI.

Otra de las principales aplicaciones de redefinir la funcin de tratamiento de errores tiene que ver con el tema anterior, los comandos deshacer en las rutinas de AutoLISP. Si nos fijamos en el cdigo de la rutina ARAND, vemos que la primera lnea es (command "_.undo" "_begin") Qu sucedera si se produce un error despus de dibujar el segundo crculo? Dara un error, y terminara el comando sin ejecutar la lnea (command "_.undo" "_end"). Con lo cual para deshacer la arandela deberiamos ejecutar el comando "H" (deshacer) dos veces, una por cada crculo. Es ms, imagina que nuestra rutina no dibuja 2 crculos sino 120 y que el error se produce al dibujar en crculo ensimo... Podemos modificar la funcin de errores: (defun ERRORES ( mens ) (setq *error* error0) (if (= mens "quitar / salir abandonar") (princ) (princ (strcat "\nError: " mens " ")) ) (command "_.undo" "_end") (if (= cmd0 1) (setvar "cmdecho" 1) ) (princ) ) Ahora, bastara con ejecutar el comando deshacer una nica vez para que se deshaga todo lo que hizo el comando ARAND. Podemos incluso decirle a la funcin ERRORES que si se produce un error, ejecute el comando deshacer directamente: (defun ERRORES ( mens ) (setq *error* error0) (if (= mens "quitar / salir abandonar") (princ) (princ (strcat "\nError: " mens " ")) ) (command "_.undo" "_end") (command "_.undo" "") (if (= cmd0 1)

(setvar "cmdecho" 1) ) (princ) ) En este caso si se produce un error ni siquiera hace falta ejecutar el comando deshacer, la funcin ERRORES ya lo hace por nosotros. Para terminar el tema, vamos a aadirle una nueva opcin a CIRCPERI. Se trata de que ofrezca por defecto el permetro del ltimo circulo dibujado. Podramos hacer lo siguiente: (defun C:CIRCPERI ( / pto peri cmd0 ) (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) (setq pto (getpoint "\nCentro de la circunferencia: ")) (if (not rad) (prompt "\nPermetro: ") (progn (prompt "\nPermetro <") (prompt (rtos (* rad 2 pi) 2 2)) (prompt ">: ") ) ) (if (setq peri (getdist)) (setq rad (/ peri (* 2 pi))) ) (command "_.circle" pto rad) (if (= cmd0 1) (setvar "cmdecho" 1) ) (princ)

) (prompt "\nNuevo comando CIRCPERI cargado") Ponemos la variable rad como global, as se puede recuperar el valor que tena en la anterior ejecucin de CIRCPERI. Veamos ahora cmo funciona el siguiente trozo de cdigo: (if (not rad) (prompt "\nPermetro: ") (progn (prompt "\nPermetro <") (prompt (rtos (* rad 2 pi) 2 2)) (prompt ">: ") ) ) Si rad es igual a nil, no se ha definido, significa que es la primera vez que se ejecuta el comando CIRCPERI en el dibujo actual. En este caso se muestra un mensaje solicitando el permetro del crculo. Si no es la primera vez que se ejecuta CIRCPERI, la variable rad tendr asociado un valor, correspondiente al radio del circulo creado en la ltima ejecucin de CIRCPERI. Tambin muestra un mensaje solicitando el permetro, pero entre los caracteres "<" y ">" se indica adems el valor de la variable global rad. (if (setq peri (getdist)) (setq rad (/ peri (* 2 pi))) ) A continuacin se solicita una distancia. No se ha indicado ningn mensaje en la funcin getdist, ya que el mensaje de solicitud se muestra en las lneas anteriores. Si se indica un permetro, ya sea por medio de dos puntos o escribiendolo directamente, entonces se calcula su radio. Si como respuesta a getdist se pulsa Intro, a la variable peri se asigna el valor nil, que es devuelto por setq. De modo que en este caso no hace nada, por lo tanto la variable rad sigue almacenando el radio del ltimo crculo creado con CIRCPERI. (command "_.circle" pto rad) Por ltimo dibuja el crculo. AutoCAD dispone de una variable de sistema llamada CIRCLERAD en la que almacena el valor del ltimo circulo dibujado. As que podemos utilizar esta variable de sistema para obtener el radio del ltimo crculo, en lugar de utilizar la variable rad como global. El cdigo sera el siguiente: (defun C:CIRCPERI ( / pto rad peri cmd0 ) (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) )

(setq pto (getpoint "\nCentro de la circunferencia: ")) (if (= (getvar "circlerad") 0.0) (prompt "\nPermetro: ") (progn (prompt "\nPermetro <") (prompt (rtos (* (getvar "circlerad")2 pi) 2 2)) (prompt ">: ") ) ) (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") Ahora ofrecer por defecto el permetro del ltimo crculo dibujado en AutoCAD, independientemente de si dicho crculo se creo con la rutina CIRCPERI, con el comando CIRCULO o por cualquier otro medio. Veamos como funcionan los cambios que hemos realizado: (if (= (getvar "circlerad") 0.0) (prompt "\nPermetro: ") (progn (prompt "\nPermetro <") (prompt (rtos (* (getvar "circlerad")2 pi) 2 2)) (prompt ">: ")

) ) Si an no se ha creado ningn crculo en el dibujo, la variable circlerad tendr asociado el valor 0.0. En este caso solicita el permetro sin ofrecer ningn valor por defecto, y en caso contrario ofrece por defecto el permetro del ltimo crculo creado. (if (setq peri (getdist)) (setq rad (/ peri (* 2 pi))) (setq rad (getvar "circlerad")) ) Si se introduce un permetro, por medio de dos puntos o escribiendolo, se calcula el radio correspondiente. En caso de que se pulse Intro, se asocia a la variable rad el radio del ltimo crculo dibujado. Limitar las respuestas de los usuarios a las funciones de solicitud de datos (I) En tema anterior vimos las funciones de tratamiento de errores, que nos permiten cotrolar lo que sucede cuando se produce un error en nuestras rutinas. Hoy intentaremos que no se produzcan alguno de los posibles errores en nuestras rutinas. Los errores pueden deberse a que el cdigo no funciona bien por que se ha empleado mal alguna funcin de AutoLISP o se halla ejecutado incorrectamente un comando de AutoCAD, por ejemplo, pero estos no son los tipos de errores que corregiremos en este tema. M s adelante veremos un tema en el que se explicarn mtodos de depuracin de nuestras rutinas, en ese tema corregiremos los errores debidos a un cdigo fuente incorrecto. En este tema trataremos otro tipo de errores, los que se producen cuando el usuario introduce datos erroneos. Por ejemplo, cuando se solicita un nmero positivo y el usuario indica cero o un nmero negativo. (INITGET [modo] [palabras_clave]) Esta funcin se utiliza para modificar el funcionamiento de otras funciones de AutoLISP, en concreto aquellas funciones en las que se solicitan datos al usuario. Por ejemplo: GETpoint, GETreal, GETint, ... casi todas comienzan por GET as que se suelen denominar funciones de tipo GET... Initget siempre devuelve nil. Y si se indica solo, sin argumentos, no hace nada. Tan solo devuelve nil. As que vamos a ver para que sirven los argumentos de Initget: "modo" Es un nmero entero que nos permitir limitar los datos que se puedan introducir en la siguiente solicitud de datos al usuario. Initget NUNCA funciona por si sola, siempre se utiliza para modificar el funcionamiento de otra funcin. El argumento modo es en realidad un cdigo binario, que puede tener los siguientes valores:

1 --> No admite valores nulos, es decir que se indique Intro como respuesta 2 --> No admite el 0 como respuesta 4 --> No admite valores negativos como respuesta 8 --> Permite indicar un punto fuera de los lmites del dibujo. An cuando estos estn activados. 16 --> Este valor no se utiliza actualmente 32 --> Dibuja la lnea o rectngulo elsticos con lneas discontnuas en lugar de contnuas

64 --> Hace que GETdist devuelva la distancia en 2D. Es como si proyectase la distancia real sobre el plano XY. 128 --> Permite introducir como respuesta una expresin de AutoLISP.

Bien, veamos como se utiliza initget... por ejemplo, si queremos que el usuario introduzca un nmero entero y que no pueda pulsar Intro como respuesta, haramos lo siguiente: (initget 1) (setq numero (getint "\nNmero entero: ")) Initget modifica a la siguiente funcin de solicitud de datos, es decir, getint. Si adems queremos que no pueda indicar 0 como respuesta, entonces sumamos sus respectivos cdigos, el 1 para que no se pueda indicar Intro como respuesta y el 2 para que no se pueda indicar 0. (initget (+ 1 2)) (setq numero (getint "\nNmero entero: ")) Tambin podemos indicar directamente (initget 3) en lugar de (initget (+ 1 2)). Si el usuario indica como respuesta 0 o Intro, AutoCAD le dir que ese dato no es vlido y que introduzca un dato correcto. Si queremos que adems se indique un nmero positivo, entoces deberiamos poner: (initget 7) (setq numero (getint "\nNmero entero: ")) Ya que 7 = 1 + 2 + 4 Veamos ahora como funciona el cdigo 8 como argumento modo de Initget. 8 --> Permite indicar un punto fuera de los lmites del dibujo. An cuando estos estn activados. Supongamos que tenemos los lmites del dibujo de AutoCAD activados (comando LIMITES) en ese caso no podemos indicar puntos fuera de esos lmites. De modo que si se solicita un punto al usuario con GETPOINT debera indicarlo dentro de los lmites del dibujo. Pero si antes de solicitar el punto se ejecuta (initget 8) entonces si nos dejara. El cdigo 16 no se utiliza. El cdigo 32 se utiliza en funciones de solicitud en las que se indica un punto base, por ejemplo: (getpoint pto "\nIndicar punto:") (getcorner pto "\nIndicar punto:") En estos casos aparece una lnea, o un rectngulo, elstico con origen en el punto base pto. Estas lneas y rectngulos elsticos se muestran con lnea continua. Pero si antes de la funcin de solicitud se aade (initget 32) se mostraran con lneas discontnuas. Por ejemplo: (setq pt1 (getpoint "\nPunto base: ")) (initget 32) (setq pt2 (getpoint pt1 "\nSegundo punto: ")) En este ejemplo aparece una lnea discontinua. Y en el siguiente un rectngulo con lneas discontinuas.

(setq pt1 (getpoint "\nPunto base: ")) (initget 32) (setq pt2 (getcorner pt1 "\nSegundo punto: ")) Veamos ahora como funciona el cdigo 64 --> 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 aadir (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 cdigo 128 permite indicar una expresin 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 podra 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. Comenzamos con CIRCPERI: (defun C:CIRCPERI ( / pto rad peri cmd0 ) (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) (setq pto (getpoint "\nCentro de la circunferencia: ")) (if (= (getvar "circlerad") 0.0) (prompt "\nPermetro: ") (progn (prompt "\nPermetro <") (prompt (rtos (* (getvar "circlerad")2 pi) 2 2)) (prompt ">: ") )

) (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") Tenemos la siguiente lnea: (setq pto (getpoint "\nCentro de la circunferencia: ")) le aadiramos alguna limitacin como respuesta del usuario? Veamoslo: Si el usuario pulsa Intro como respuesta a la variable pto se le asocia el valor nil, que es lo que devolvera Getpoint. Despus al intentar dibujar el crculo (command "_.circle" pto rad) se producira un error. As que debemos impedir que el usuario introduzca Intro como respuesta, de modo que aadiramos (initget 1) antes de la funcin getpoint. Como estamos solicitando un punto, el cdigo 2 no tiene sentido y lo mismo sucede con el 4. El cdigo 8 permite indicar un punto fuera de los lmites del dibujo, an cuando estos estn activados. Este cdigo si podramos utilizarlo, aunque si el usuario trabaja con los lmites activados, estn activados y ya est. Si quiere que los desactive l, no? Porque supongo que los tendr activados por algn motivo. As que no le aadimos a initget el cdigo 8. El cdigo 32 no tiene sentido aqui, pues no aparece la lnea base. Y el cdigo 64 tampoco ya que estamos solicitando un punto, no una distancia. El cdigo 128 permite introducir como respuesta una expresin de AutoLISP. Este cdigo tambin se podra indicar, pero lo habitual es no hacerlo. Se podra indicar en casos en los que el usuario pudiera utilizar una expresin 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. Aqui nos pide un punto, as que no tiene demasiado sentido. Definitivamente el cdigo quedara: ... (initget 1) (setq pto (getpoint "\nCentro de la circunferencia: ")) ...

Sigamos modificando la rutina CIRCPERI... Unas lneas despus de la sollicitud del centro de la circunferencia, se solicita su permetro: (if (setq peri (getdist)) (setq rad (/ peri (* 2 pi))) (setq rad (getvar "circlerad")) ) Aadimos el cdigo 1 antes de la funcin getdist? Veamos como funciona esta parte del cdigo: Si el usuario introduce una distancia, se evala la condicin 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 evala la condicin no cumple del IF, es decir: (setq rad (getvar "circlerad")) Si ponemos (initget 1), el usuario no podr indicar Intro, as que nunca se ejecutara la expresin no cumple. Por tanto no aadimos el cdigo 1 a Initget. El permetro del cculo no puede ser ni cero ni un nmero negativo, de modo que podemos aadir a initget los cdigos 2 y 4. Tambin podramos aadirle el cdigo 64 para que GETdist devuelva la distancia en 2D. Pero normalmente no conviene aadir este cdigo, excepto cuando la distancia "debe" siempre ser en 2D. Una ltima nota sobre CIRCPERI... Cuando ejecutamos la rutina por primera vez en un dibujo en el que no se halla dibujado ningn crculo, la variable de sistema "circlerad" tiene el valor 0.0. En este caso, no ofrecemos la opcin de seleccionar el radio del ltimo crculo dibujado pulsando Intro, ya que no existe ningn crculo dibujado previamente. En este caso, no deberamos 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 cdigo 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 "\nPermetro: ") (initget 7) ) (progn

(prompt "\nPermetro <") (prompt (rtos (* (getvar "circlerad")2 pi) 2 2)) (prompt ">: ") (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: (defun C:ARAND ( / pto rad cmd0 ) (command "_.undo" "_begin") (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) (setq pto (getpoint "\nCentro de la arandela: ")) (setq rad (getreal "\nRadio interior: ")) (command "_.circle" pto rad) (setq rad (getreal "\nRadio exterior: ")) (command "_.circle" pto rad)

(if (= cmd0 1) (setvar "cmdecho" 1) ) (command "_undo" "_end") (princ) ) (prompt "\nNuevo comando ARAND cargado") La primera solicitud que tenemos en ARAND es la del centro de la arandela. Practicamente es igual que la solicitud del centro del cculo en CIRCPERI, as que le aadimos tambin el cdigo 1 a Initget: (initget 1) (setq pto (getpoint "\nCentro de la arandela: ")) A continuacin solicita los radios interior y exterior de la arandela. En los que podemos evitar que el usuario indique como respuesta Intro, 0 o un nmero negativo. De modo que aadiriamos Initeget con el modo 7. Veamos el cdigo 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 (getreal "\nRadio interior: ")) (command "_.circle" pto rad) (initget 7) (setq rad (getreal "\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 a las funciones de solicitud de datos (y II) En el ltimo tema comenzamos a ver la funcin INITGET y vimos como funcionaba el argumento [modo] ahora vamos con [palabras_clave]... (initget [modo] [palabras_clave]) [palabras_clave] nos permite indicar una serie de textos (palabras) que tambin seran aceptados como respuesta en la siguiente funcin de solicitud de datos. Por ejemplo: (initget 7 "Diametro Perimetro") (setq rad (getdist "\nRadio del circulo / Diametro / Perimetro :")) como respuesta a getdist podemos indicar una distancia, ya sea escribiendola o mediante dos puntos, pero ahora tambin aceptara como respuesta Diametro y Perimetro. El modo 7 impide que se indique como respuesta Intro, cero o un nmero negativo.

Si indicamos como respuesta una distancia, asocia esa distancia a la varible rad. Si indicamos como respuesta Diametro, asocia a la varible rad la cadena de texto "Diametro". Si indicamos como respuesta Perimetro, asocia a la varible rad la cadena de texto "Perimetro".

En [palabras_clave] indicamos una serie de palabras, separadas por espacios, que serviran como respuesta a la siguiente funcin 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 maysculas. Es decir, la D o la P. En el siguiente ejemplo... (initget 7 "Diametro DEShacer") (setq rad (getdist "\nRadio del circulo / Diametro / DEShacer: ")) Para seleccionar la opcin Diametro habr que escribir al menos la D. Pero para seleccionar DEShacer al menos habr que escribir DES. Tambin aceptara dia para el dimetro y desha para deshacer. Fjate que al menos deben indicarse las letras en maysculas. Supongamos que tenemos el siguiente cdigo: (initget 7 "Diametro Perimetro") (setq rad (getdist "\nRadio del circulo: ")) En este caso getdist aceptara como respuestas a la peticin del radio del crculo "Diametro" y "Perimetro". Pero al no indicar estas opciones en el mensaje de getdist, el usuario no sabra que existen. De modo que lo deseable es indicar al usuario las opciones que puede seleccionar: (initget 7 "Diametro Perimetro")

(setq rad (getdist "\nRadio del circulo / Diametro / Perimetro :")) Como mejor se ve es con un ejemplo as que... Vamos a modificar la rutina ARAND aadiendo una opcin para indicar los dimetros en lugar del radio. Tenamos el siguiente cdigo: (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 (getreal "\nRadio interior: ")) (command "_.circle" pto rad) (initget 7) (setq rad (getreal "\nRadio exterior: ")) (command "_.circle" pto rad) (if (= cmd0 1) (setvar "cmdecho" 1) ) (command "_undo" "_end") (princ) ) (prompt "\nNuevo comando ARAND cargado") Tenemos que modificar las siguientes lneas: (initget 7) (setq rad (getreal "\nRadio interior: ")) Primero aadimos "Diametro" a la lista de palabras clave de initget: (initget 7 "Diametro")

Y a continuacin le decimos al usuario que existe una opcin llamada Diametro que puede seleccionar como respuesta: (setq rad (getreal "\nRadio interior / Diametro: ")) De esta forma ya hemos aadido la opcin Diametro, pero Qu sucede si el usuario indica como respuesta la opcin Diametro? Pues que asignariamos a la variable rad la cadena de texto "Diametro" y en la siguiente lnea (command "_.circle" pto rad) al intentar dibujar el crculo, nos dara un error. As que hay que modificar la rutina aadiendo a continuacin algo parecido a: Si el usuario selecciona la opcin Diametro --> Preguntamos por el dimetro. Pues esto en AutoLISP, sera: (if (= rad "Diametro") (setq rad (getreal "\nDimetro interior: ")) ) Hay que tener especial cuidado con la condicin del IF (= rad "Diametro") ya que hay que indicar la palabra clave tal y como aparece en la lista de palabras clave de initget. Es decir, no funcionara si ponemos (= rad "diametro") o (= rad "Dimetro") ya que en el primer caso no ponemos la "D" en maysculas y en el segundo hemos tildado la "a". Fijemonos en lo que hace el cdigo anterior: Si el usuario indica una distancia la asigna a la variable rad y luego (if (= rad "Diametro")... devuelve nil, puesto que rad es distinto de "Diametro". Si el usuario indica D o Diam, o diametro, entonces asigna a la variable rad la cadena de texto "Diametro". Luego al entrar en el IF, (= rad "Diametro") devuelve T as que evala la expresin si cumple (setq rad (getreal "\nDimetro interior: ")) que pide un dimetro y lo asignamos a la variable rad. Pero rad viene de radio, porque en esta variable almacenamos el radio del crculo y no el dimetro. As que al dibujar el crculo (command "_.circle" pto rad) dibujara un circulo del doble del dimetro de lo que ha dicho el usuario. El cdigo debera ser: (initget 7 "Diametro") (setq rad (getreal "\nRadio interior / Diametro: ")) (if (= rad "Diametro") (setq rad (/ (getreal "\nDimetro interior: ") 2.0)) ) Pero, Qu pasa si el usuario hace lo siguiente? 1. 2. Indica D como respuesta al radio interior. Indica -50 o 0 como dimetro.

Pues que asignara a la variable rad el resultado de dividir -50 o 0 entre 2.0. Por tanto tendriamos un crculo con radio negativo o cero. Recuerda que initget solo tiene efecto sobre la siguiente funcin de solicitud de datos, de modo que tenemos que aadir de nuevo la funcin Initget antes de preguntar por el dimetro: (initget 7 "Diametro") (setq rad (getreal "\nRadio interior / Diametro: "))

(if (= rad "Diametro") (progn (initget 7) (setq rad (/ (getreal "\nDimetro interior: ") 2.0)) ) ) Se ha aadido la funcin Progn ya que sino, solo puedo ejecutar una expresin. Para el radio o dimetro exterior se hara exactamente lo mismo. Por tanto el cdigo completo sera: (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 "Diametro") (setq rad (getreal "\nRadio interior / Diametro: ")) (if (= rad "Diametro") (progn (initget 7) (setq rad (/ (getreal "\nDimetro interior: ") 2.0)) ) ) (command "_.circle" pto rad) (initget 7 "Diametro") (setq rad (getreal "\nRadio exterior / Diametro: ")) (if (= rad "Diametro") (progn

(initget 7) (setq rad (/ (getreal "\nDimetro 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 funcin de AutoLISP se utiliza para obtener una opcin indicada por el usuario. Se utiliza en combinacin con INITGET, por ejemplo: (initget "DEShacer Nuevo Repetir") (setq opc (getkword "\nDEShacer / Nuevo / Repetir: ") En este caso el usuario tan solo podr indicar como respuesta una de las palabras clave de la funcin 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 nmero negativo. Pero no es obligatorio indicar siempre un modo. Un ejemplo bastante habitual en las rutinas es el siguiente: (initget "DEShacer Nuevo Repetir") (setq opc (getkword "\nDEShacer / Nuevo / Repetir: ")) (cond ((= opc "DEShacer") (alert "Has seleccionado la opcin DEShacer") ) ((= opc "Nuevo") (alert "Has seleccionado la opcin Nuevo")

) (T (alert "Has seleccionado la opcin Repetir") ) ) As en funcin de lo que indique el usuario se hace una cosa u otra. Estructuras repetitivas Hasta hace poco tan solo podiamos crear programas cuya ejecucin fuera lineal: 1. 2. 3. Haz esto Ahora esto otro ...

Luego vimos las estructuras condicionales IF y COND que ya nos permiten jugar un poco ms y hacer que nuestros programas no sean tan lineales. Ahora vamos a ver funciones que nos permitiran crear repeticiones de cdigo y algo que tal vez te suene, los bucles, que se utilizan mucho en programacin. (WHILE condicin [expr1] [expr2] ...) La funcin while ejecuta las expresiones indicadas MIENTRAS se cumpla la condicin, y devuelve el valor de la ltima expresin evaluada. Por ejemplo: (setq i 0) (while (< i 10) (prompt "\n") (prompt (itoa i)) (setq i (1+ i) ) Mientras i sea menor que 10, ejecuta las expresiones. Es decir, escribira en la ventana de comandos de AutoCAD los nmeros del 0 al 9. Cuando (setq i (1+ i) asigna a la variable i el valor 10, la condicin del bucle no se verifica, de modo que termina el bucle. La funcin While devolver el valor de la ltima expresin evaluada, es decir 10. Este es un tpico ejemplo de bucle, una estructura repetitiva con un ndice o contador, en este caso i, que puede ir aumentando o disminuyendo. Fjate que una de las expresiones que se ejecutan dentro del bucle es (setq i (1+ i) es decir, movemos el contador. Si no lo hicieramos, i siempre sera menor que 10 y se entrara en un bucle sin fin, que da lugar a un error. Veamos el siguiente ejemplo: (setq i 10) (while (< i 10)

(prompt "\n") (prompt (itoa i)) (setq i (1+ i) ) En este caso i tiene asignado el valor 10 antes de entrar en el bucle, de modo que la condicin (< i 10) no se cumplira y por tanto no se ejecutarian las expresiones siguientes. While devuelve el valor de la ltima expresin evaluada, que en este caso es la condicin as que devuelve nil. Las expresiones en While son opcionales, de modo que podemos crear un bucle en el que solo se indique la condicin: (while (not (setq pt (getpoint "\nPunto inicial: ")))) En este caso la condicin es (not (setq pt (getpoint "\nPunto inicial: "))) es decir, pide un punto al usuario y lo almacena en la variable pt.

Si el usuario indica un punto, pt = (X Y Z) que es distinto de nil, de modo que (not (setq pt (getpoint "\nPunto inicial: "))) devolver nil y saldr de la funcin While. Si el usuario indica Intro, getpoint devolver nil y lo almacenar en la variable pt, de modo que (not (setq pt (getpoint "\nPunto inicial: "))) devolvera T, y preguntara de nuevo por un punto.

Por lo tanto, mientras no se indica un punto, sigue preguntando. Otro ejemplo tpico de bucles es en el que se utilizan algunas variables como flags (banderas o banderillas) para indicar si algo est activado o desactivado o para controlar distintos valores. Veamos un ejemplo, supongamos que a y b son dos variables que almacenan dos nmeros reales: (if (< a b) (setq flag1 nil) (setq flag1 T) ) (while flag1 (prompt "\na NO es menor que b") (setq b (getreal "\nIntroduzca un nmero: ")) (if (< a b) (setq flag1 nil) ) ) En este caso:

Si a es menor que b --> flag1 = nil Si a NO es menor que b --> flag1 = T

Luego la funcin While evala la condicin falg1 que devolvera su valor nil o T.

Si es flag1 = nil (a es menor que b) no evala las expresiones pues no se verifica la condicin. Si es flag1 = T (a NO es menor que b) se verifica la condicin as que se ejecutan las expresiones. Primero nos dice que a no es menor que b, nos vuelve a pedir el valor de b y comprueba si a es menor que el nuevo valor de b en cuyo caso asigna a la banderilla flag1 el valor nil para salir del bucle.

Es decir, mientas a NO sea menor que b, nos pedir un nuevo valor de b. En la funcin While, al igual que vimos con IF y COND, como condicin podemos utilizar expresiones lgicas. Por ejemplo: (while (or (< a b) (< b 0.0)) (prompt "\na NO es menor que b, o b es negativo") (setq b (getreal "\nIntroduzca un nmero positivo: ")) ) En este ejemplo, el bucle se ejecutar hasta que se indique un valor para b positivo y mayor que a. (REPEAT cantidad [expr1] [expr2]) La funcin repeat ejecuta las expresiones indicadas el nmero de veces que se indique en cantidad. Devuelve el resultado de la ltima expresin evaluada. (repeat 10 (propmt "Este curso es demasiado fcil para mi")) Tambin podramos asignar la cantidad a repetir a una variable: (setq Bart_Simpson (getint "\nNmero de repeticiones para Bart: ")) (repeat Bart_Simpson (prompt "\nNo volver a hacer pompas con cido sulfrico en clase")) En este caso escribira en la pizarra, es decir la ventana de comandos de AutoCAD, esa frase tantas veces como le indiquemos. Tambin podemos obtener la cantidad por medio de una funcin de AutoLISP, como resultado de una operacin: (repeat (+ 4 5) (propmt "Este curso me parece poco serio")) o suponiendo que las variables a y b tengan asignados dos nmeros enteros: (propmt "Inspector Gadget, este mensaje se autodestruir en...") (setq i 0) (repeat (+ a b) (propmt "\t") (propmt (itoa i)) (setq i (1+ i))

) Pero qu sucede si a o b son un nmero real y no un entero? repetir las expresiones 2.5 veces? Pues no, nos dar un error. Por eso hay que estar bien seguro de que se la cantidad indicada es un nmero entero y no un real. Incluso si como cantidad indicamos un nmero entero sin decimales, como 2.0, nos dara un error. Vamos a modifuicar la rutina ARAND para hacer que el segundo radio sea mayor que el primero. El cdigo correspondiente al crculo exterior era el siguiente: (initget 7 "Diametro") (setq rad (getreal "\nRadio exterior / Diametro: ")) (if (= rad "Diametro") (progn (initget 7) (setq rad (/ (getreal "\nDimetro exterior: ") 2.0)) ) ) (command "_.circle" pto rad) Lo primero que vamos a cambiar es el nombre de las variables. En lugar de utilizar la variable rad tanto para el radio interior como para el exterio, vamos a utilizar la variable radi para el radio interior y la variable rade para el exterior. As podremos comparar si rade es mayor que radi. Podriamos sustituir el cdigo anterior por el siguiente: (while (or (not rade) (not (< radi rade))) (initget 7 "Diametro") (setq rad (getreal "\nRadio exterior / Diametro: ")) (if (= rad "Diametro") (progn (initget 7) (setq rad (/ (getreal "\nDimetro exterior: ") 2.0)) ) ) ) (command "_.circle" pto rade)

La primera vez que se evala la condicin del bucle, no se ha asignado an ningn valor al radio exterior. De modo que rade = nil y (not rade) devolver T. (or (not rade) (not (< radi rade))) comprueba que al menos se verifique una de las dos condiciones. Al verificarse la primera condicin (not rade) la segunda ni siquiera se evala (por suerte puesto que al no estar definida rade nos dara un error). La condicin se verifica y ejecuta las expresiones que estn a continuacin, que nos piden un valor para el radio exterior. Es decir, mientras no se indique el radio exterior o este sea menor que el radio interior se ejecuta el bucle, que nos pide un nuevo valor para el radio exterior. Al salir del bucle ya tenemos un radio exterior vlido as que dibujamos el crculo exterior. El cdigo completo sera: (defun C:ARAND ( / pto radi rade cmd0 ) (command "_.undo" "_begin") (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) (initget 1) (setq pto (getpoint "\nCentro de la arandela: ")) (initget 7 "Diametro") (setq radi (getreal "\nRadio interior / Dimetro: ")) (if (= radi "Diametro") (progn (initget 7) (setq radi (/ (getreal "\nDimetro interior: ") 2.0)) ) ) (command "_.circle" pto radi) (while (or (not rade) (not (< radi rade))) (initget 7 "Diametro") (setq rade (getreal "\nRadio exterior / Dimetro: ")) (if (= rade "Diametro") (progn (initget 7) (setq rade (/ (getreal "\nDimetro exterior: ") 2.0)) )

) ) (command "_.circle" pto rade) (if (= cmd0 1) (setvar "cmdecho" 1) ) (command "_.undo" "_end") (princ) ) (prompt "\nNuevo comando ARAND cargado") Funciones para manipular cadenas de texto En este tema veremos las funciones que incorpora AutoLISP para manipular cadenas de texto. (CHR entero) Esta funcin devuelve el caracter al que le corresponde el cdigo ASCII inidicado. Por ejemplo: (chr 65) devuelve "A" (chr 97) devuelve "a" (ASCII texto) Devuelve el cdigo ASCII (un nmero entero) correspondiente al primer caracter de la cadena de texto indicada. (ascii "Abcde") devuelve 65 (ascii "A") tambin devuelve 65 porque lo nico que importa es el primer caracter de la cadena de texto. (STRLEN texto1 [texto2] ...) Esta funcin devuelve la longitud (nmero de caracteres) de la cadena de texto que recibe como argumento. (strlen "AutoLISP") devuelve 8 Tambin podemos asignar una cadena de texto a una variable y comprobar despus su longitud con strlen: (setq txt1 "AutoLISP") (strlen txt1) Si se indica ms de una cadena de texto, devolver la suma de sus longitudes: (strlen "AutoLISP" "AutoCAD") devuelve 15

(STRCASE texto [modo]) Se utiliza para convertir un texto en maysculas o minsculas. Por ejemplo: (strcase "AutoLISP") devuelve "AUTOLISP" Si se indica el argumento opcional [modo] con un valor distinto de nil, entonces devuelve el texto en minsculas. (strcase "AutoLISP" T) devuelve "autolisp" (STRCAT texto1 [texto2] ...) Esta funcin concatena (une) varias cadenas de texto. (strcat "Hola" "Mundo") devolvera "HolaMundo" Para que las dos palabras anteriores aparezcan separadas se pueden hacer dos cosas: Aadir el espacio en una de las palabras: (strcat "Hola " "Mundo") Aadir el espacio como otra cadena de texto: (strcat "Hola" " " "Mundo") Esta funcin se utiliza mucho para concatenar los mensajes que se muestran al usuario. Por ejemplo: (setq i 10) (setq rad (getreal (strcat "\nRadio <" (itoa i) ">: "))) Mostrar el siguiente mensaje en la ventana de AutoCAD: Radio <10>: (SUBSTR texto pos_inicial [longitud]) Esta funcin devuelve parte de la cadena de texto que recibe como argumento, a partir de la posicin inicial que se indique en adelante. (substr "AutoLISP" 5) devuelve el texto a partir del quinto caracter, es decir "LISP". (substr "AutoLISP" 1) devuelve el texto entero. (substr "AutoLISP" 15) devuelve "" es decir, una cadena de texto vaca puesto que el texto indicado tiene menos de 15 caracteres. Tambin se puede indicar la longitud, es decir el nmero de caracteres que se desean obtener. (substr "AutoLISP" 5 2) devuelve "LI". A partir del quinto caracter devuelve 2 caracteres. (substr "AutoLISP" 5 10) devolver "LISP", porque tan solo tenemos 4 caracteres ms, a partir del quinto, y no diez. (WCMATCH cadena patrn) Esta funcin se utiliza para comparar si una cadena de texto verifica o cumple un patrn, en caso de que lo verifique devuelve T y si no lo verifica devuelve nil. (wcmatch "AutoLISP" "A*") devolver T ya que comprueba si "AutoLISP" comienza por "A".

(wcmatch "AutoLISP" "a*") devolver nil, puesto que "A" es distinto de "a". Los patrones son cadenas de texto en las que se pueden emplear determinados smbolos comodn: # Equivale a un dgito. @ Equivale a una letra. . Equivale a un caracter que no sea alfanumrico (ni letras ni nmeros). * Equivale a una cadena de caracteres. ? Equivale a un caracter cualquiera. ~ Equivale a una negacin. Por ejemplo (wcmatch "AutoLISP" "~B*") comprueba que el texto no empieza por "B". [...] Nos permite indicar varios caracteres. Por ejemplo (wcmatch "AutoLISP" "[AB]*") comprueba si el texto comienza por "A" o por "B". Tambin podramos indicar un rango: (wcmatch "AutoLISP" "[A-F]*") comprueba si el texto comienza por "A", "B", "C", ... hasta la "F". Incluso podemos comprobar con dos patrones distintos, separandolos por una coma: (wcmatch "AutoLISP" "A*,*LISP") devolvera T. Pero cmo hariamos entonces para saber si un texto tiene una coma? no podemos hacer los siguiente: (wcmatch "Curso, de AutoLISP" "*,*") ya que en este caso le estamos indicando dos patrones, como en el ejemplo anterior. Tendremos que hacerlo as: (wcmatch "Curso, de AutoLISP" "*',*") al anteponer el apstrofe ' delante de uno de los caracteres comodines de los patrones, le estamos diciendo que queremos usar el literal, es decir, lo que ponemos a continuacin tal cual est. Pues tema visto. As que pasamos ahora a Modificar CIRCPERI. Tenamos el siguiente cdigo: (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 "\nPermetro: ") (initget 7) ) (progn

(prompt "\nPermetro <") (prompt (rtos (* (getvar "circlerad")2 pi) 2 2)) (prompt ">: ") (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") Fijemonos en las siguientes lneas de la funcin: (prompt "\nPermetro <") (prompt (rtos (* (getvar "circlerad")2 pi) 2 2)) (prompt ">: ") Podriamos concatenar las cadenas de texto y ejecutar una nica vez la funcin prompt: (prompt (strcat "\nPermetro <" (rtos (* (getvar "circlerad") 2 pi) 2 2) ">: ")) Vamos ahora a crear un nuevo comando llamado CIRCOLOM que nos permitir dibujar mltiples crculos concntricos. Y comenzaremos como siempre por el pseudocdigo. Podra ser algo as: 1. 2. Pedir el centro de los crculos. Mientras se indique un punto o radio. 1.

Al escribir el cdigo, lo primero es definir el nuevo comando:

(defun C:CIRCULOM ( / ) De momento no ponemos las variables locales, porque an no tenemos ni variables ni nada. Las aadiremos al terminar la rutina. 1. Pedir el centro de los crculos. Por ejemplo as: (setq pto (getpoint "\nCentro: ")) Tal vez pienses... no deberiamos ponerle antes un INITGET? Pues seguramente, pero no nos paremos ahora con eso. Primero a ver si hacemos que funcione la rutina, despus ya nos preocuparemos de que funcione bien. 2. Mientras se indique un punto o radio. Lo podemos hacer con While: (while (setq rad (getdist pto "\nRadio [Intro para terminar]: ")) Pide puntos o distancias, para usarlas como radio, hasta que se pulse Intro. Fijate que no he cerrado el while, porque an tenemos que aadir las expresiones que van dentro. Y nos falta solo... 2.1. Dibujar el crculo. As de sencillo: (command "_.circle" pto rad) nos faltan dos cosas para terminar: El parntesis de cierre de While y el de la funcin. El cdigo completo queda as: (defun C:CIRCULOM ( / ) (setq pto (getpoint "\nCentro: ")) (while (setq rad (getdist pto "\nRadio [Intro para terminar]: ")) (command "_.circle" pto rad) ) ) Ahora es cuando vamos a introducir las mejoras y a retocar el cdigo. Vamos a comenzar limitando las posibles respuestas de los usuarios con Initget: (defun C:CIRCULOM ( / ) (initget 1) (setq pto (getpoint "\nCentro: ")) (initget 6) (while (setq rad (getdist pto "\nRadio [Intro para terminar]: ")) (command "_.circle" pto rad) (initget 6) )

) Aadimos (initget 1) antes de la solicitud Getpoint, para que el usuario no pueda indicar Intro como respuesta. Y aadimos (initget 6) antes de getdist para que no permita ni cero ni un nmero negativo. Observaras que (initget 6) se vuelve a poner otra vez dentro de While. Si no lo hicieramos, al indicar el primer radio no permitira responder con cero ni con un nmero negativo. Sin embargo, para el segundo radio y todos los siguientes tenemos si nos dejara. Para que no lo permita, hay que incluir la funcin Initget antes de que se ejecute de nuevo Getdist, de modo que se aade dentro de While. Otras mejoras sera:

Aadir las variables a la lista de variables locales de la funcin. Desactivar el eco de mensajes. Aadir una salida limpia a la funcin. Aadir una lnea fuera de la funcin para que indique el nombre del comando al cargar la rutina. Aadir el comando Deshacer Inicio al principiio de la rutina y Deshacer Fin al terminarla.

(defun C:CIRCULOM ( / pto rad cmd0 ) (command "_.undo" "_begin") (if (= (setq cmd0 (getvar "cmdecho")) 1) (setvar "cmdecho" 0) ) (initget 1) (setq pto (getpoint "\nCentro: ")) (initget 6) (while (setq rad (getdist pto "\nRadio [Intro para terminar]: ")) (command "_.circle" pto rad) (initget 6) ) (if (= cmd0 1) (setvar "cmdecho" 1) ) (command "_.undo" "_end") (princ) )

(prompt "\nNuevo comando CIRCULOM cargado") Trabajar con ngulos y distancias AutoLISP nos proporciona una serie de funciones para trabajar con ngulos y distancias, comenzaremos viendo los angulos: (ANGLE pt1 pt2) Esta funcin devuelve el ngulo formado por la lnea que va desde pt1 hasta pt2. El origen de angulos ser el eje X y el sentido antihorario se considera positivo. Por ejemplo: (setq pt1 (getpoint "\nPunto1: ")) (setq pt2 (getpoint pt1 "\nPunto2: ")) (setq ang (angle pt1 pt2)) Hay que fijarse en cual es el punto que se indica primero en la funcin angle, porque si hacemos: (setq ang2 (angle pt2 pt1)) Obtendremos un ngulo distinto, el anterior ms pi o menos pi. Por lo tanto: No es lo mismo el ngulo de pt1 a pt2 que el ngulo de pt2 a pt1. Si los puntos estn en 3D, los proyecta sobre el plano XY y devuelve el ngulo formado por sus proyecciones (DISTANCE pt1 pt2) Devuelve la distancia en 3D entre los puntos pt1 y pt2. Si uno de los puntos est en 2D, es decir no tiene coordenada Z, se ignora la coordenada Z del otro punto devolviendo la distancia en 2D. (setq pt1 (getpoint "\nPunto1: ")) (setq pt2 (getpoint pt1 "\nPunto2: ")) (setq dist12 (distance pt1 pt2)) En este caso, (setq dist21 (distance pt2 pt1)) sera igual a dist12. Ya que la distancia del punto pt1 al pt2 es igual que la distancia del punto pt2 al pt1. (POLAR ptobase ang dist) Esta funcin se utiliza para obtener un punto por medio de coordenadas polares a partir de un punto base. Por ejemplo: (setq pt1 (getpoint "\nPunto1: ")) (setq pt2 (polar pt1 0.0 50.0)) Esto asignara a la variable pt2 un punto que est a 50 unidades en la direccin del eje X a partir del punto base pt1. Si hicieramos: (command "_.line" pt1 pt2 "") Dibujaramos una lnea horizontal de 50 unidades, desde pt1 hasta pt2.

Hay que observar que en (polar pt1 0.0 50.0) se indica primero el punto base, luego un ngulo en radianes y por ltimo una distancia. De modo que: (setq pt2 (polar pt1 (/ pi 2.0) 50.0)) Devolver un punto que est a 50 unidades en la direccin del eje Y a partir del punto base pt1. Ya que en este caso se ha indicado un ngulo de (/ pi 2.0) es deirc, 90. Funciones avanzadas para trabajar con listas Ya hemos visto algunas funciones para manejar listas: CAR, CDR, LIST... pero hay muchas ms. En este tema veremos algunas de las que nos faltan. (LAST lista) Esta funcin devuelve el ltimo elemento de la lista que recibe como argumento. De modo que si hacemos: (setq pto (getpoint "\nPunto de insercin: ")) (setq z (last pto)) En la variable z almacenamos la coordenada Z del punto pto, siempre que el punto est en 3D porque si est en 2D almacenara la coordenada Y. Los puntos son listas del tipo (10.0 20.0 0.0). El ejemplo anterior no sera muy til para obtener la coordenada Y, ya que si indicamos un punto en 3D devolver la coordenada Z. Entonces, cmo podemos obtener la coordenada Y? ya vimos un mtodo: (CADR pto) Pero supongamos que tenemos una lista de 27 elementos, y queremos obtener el elemento 22. Cmo lo haramos? No podemos utilizar (cADDD.....DDDr pto) ya que solo nos permite agrupar 4 funciones cAr y cDr. De modo que tendriamos que hacer (cADDD (cDDDD (cDDDDr ..... pto). Pero hacerlo as bastante engorroso. (NTH numero lista) Devuelve el elemento cuyo nmero de orden se indica de la lista. (nth 2 pto) devolver la coordenada Y, no? Pues no!. Los elementos de una lista se empiezan a numerar desde cero. Por ejemplo: (nth 0 pto) devolver la coordenada X del punto pto. (nth 1 pto) devolver la coordenada Y del punto pto. (nth 2 pto) devolver la coordenada Z del punto pto, si existe y si no existe devolver nil. Supongamos que tenemos una lista como la siguiente: (setq lst (list "peras" "melones" "sandias" "perros" "platanos")) para comprobar si "perros" pertenece a la lista lst se utiliza la funcin MEMBER. (MEMBER elemento lista) Con esta funcin, si el elemento indicado pertenece a la lista devolver la lista a partir de ese elemento. Por ejemplo: (member "perros" lst) devolver ("perros" "platanos")

Si el elemento no pertenece a la lista, devolver nil. (member "gatos" lst) devuelve nil (SUBST elem_nuevo elem_antiguo lista) Esta funcin reemplaza un elemento de una lista por su nuevo valor. Por ejemplo, para reemplazar "perros" por "naranjas": (subst "naranjas" "perros" lst) devuelve la lista con el elemento que se ha modificado ("peras" "melones" "sandias" "naranjas" "platanos"). Pero la variable lst sigue almacenando la lista anterior ("peras" "melones" "sandias" "perros" "platanos"). Para modificar la lista almacenada en la variable lst tenemos que asignarle el valor devuelto por SUBST: (setq lst (subst "naranjas" "perros" lst)) Supongamos que queremos aadir un nuevo elemento "limones" a la siguiente lista: (setq lst (list "peras" "naranjas" "manzanas")) Podriamos hacerlo con list, pero sera un lio: (setq lst (list (car lst) (cadr lst) (caddr lst) "limones")) Para una lista de 3 elementos se puede hacer, pero... Y si tenemos 30 elementos? (CONS elemento lista) Esta funcin aade un elemento a la lista indicada. El nuevo elemento se sita en el primer lugar. Es decir: (cons "limones" lst) devolver ("limones" "peras" "naranjas" "manzanas") Y al igual que suceda con SUBST, si solo hacemos (CONS "limones" lst) no estamos asignando la lista resultante a la variable lst. Deberiamos hacerlo as: (setq lst (cons "limones" lst)) Pero... y si lo que queremos es aadir un elemento al final de la lista? Antes de ver como sera, veamos otra funcin: (REVERSE lista) Devuelve la lista que recibe como argumento pero en orden inverso. (reverse lst) devolver ("manzanas" "naranjas" "peras" "limones") Bien, pues para aadir un elemento al final de una lista... (setq lst (reverse (cons "platanos" (reverse lst)))) Veamos como funciona la lnea de cdigo anterior: (reverse lst) devuelve la lista en orden inverso, (cons "platanos" (reverse lst)) aade "platanos" como primer elemento de la lista invertida, y por ltimo volvemos a invertir el orden de la lista: (setq lst (reverse (cons "platanos" (reverse lst)))) (ACAD_STRLSORT lista)

Esta funcin devuelve una lista con sus elementos, que deberan ser cadenas de texto, ordenados alfabticamente. Por ejemplo: ((acad_strlsort lst) devolver ("limones" "manzanas" "naranjas" "peras" "platanos") Si tenemos dos listas: (setq lst1 (list "peras" "naranjas" "manzanas")) (setq lst2 (list "fresas" "limones")) y que queremos unirlas... (APPEND lista1 [lista2] ...) Esta funcin une dos o ms listas y devuelve la lista resultante. (append lst1 lst2) devolver ("peras" "naranjas" "manzanas" "fresas" "limones") Los pares punteados Los pares punteados son simplemente listas de dos elementos separadas por un punto. Por ejemplo: (0 . 27) Qu diferencia a un par punteado de una lista normal? Veamoslo: 1. 2. Aunque el punto nos pueda engaar, tienen solo dos elementos. En este caso 0 y 27. Podemos obtener el primer elemento, como en cualquier lista, mediante (car par_punteado). Pero, si intentamos obtener el segundo elemento mediante (cadr par_punteado) nos indicar "error: tipo de argumento errneo:" Para obtener el segundo elemento utilizaremos CDR en lugar de CADR (cdr par_punteado). Ojo! (cdr par_punteado) devolver 27 y no (27).

Para qu se usan los pares punteados? Pues principalmente para crear listas de asociaciones. Y qu es una lista de asociaciones? Pues una lista cuyos elementos son pares punteados. Por ejemplo: (("perro" . 0) ("gato" . 1) ("ratn" . 5)) Al primer elemento de los pares punteados le denominaremos cdigo, y al segundo elemento valor. En el ejemplo anterior "perro", "gato" y "ratn" son los cdigos y 0, 1 y 5 sus respectivos valores. En una lista de asociaciones, generalmente no existen cdigos duplicados. Un ejemplo de lista de asociacin con un cdigo duplicado sera: (("perro" . 0) ("gato" . 1) ("ratn" . 5) ("gato" . 7)) Siendo "gato" el cdigo que est duplicado. Cmo se crear los pares punteados? Con la funcin que ya hemos visto, CONS. Antes se explico (CONS elemento lista) que nos permite aadir un elemento nuevo como primer elemento de la lista indicada. Si en lugar de una lista se indica otro elemento creamos un par punteado. (CONS cdigo valor) (setq par_punteado (cons 7 "sandias")) asignar a par_punteado (7 . "sandias") Para crear una lista de asociaciones, por ejemplo: (setq lst (list (cons "perro" 0) (cons "gato" 1) (cons "ratn" 5)))

Por ltimo veamos una funcin que utilizaremos mucho, sobre todo al trabajar con las entidades de AutoCAD... (ASSOC cdigo lista_de asociaciones) ASSOC nos servir para obtener el par punteado, cuyo cdigo se indique, de la lista de asociaciones que recibe como argumento. Por ejemplo: (assoc "gato" lst) devolver ("gato" . 1) Si lo que queremos obtener es el valor asociado a ese cdigo, es decir de ("gato" . 1) queremos obtener el 1. Entonces, podemos hacer lo siguiente: (cdr (assoc "gato" lst)) Pero, Por qu son tan importantes las listas de asociaciones y los pares punteados? Por lo siguiente: ((-1 . <Nombre de entidad: 19e0958>) (0 . "LINE") (330 . <Nombre de entidad: 19e08f8>) (5 . "2B") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "0") (100 . "AcDbLine") (10 0.0 0.0 0.0) (11 100.0 100.0 0.0) (210 0.0 0.0 1.0)) Esta lista de asociaciones, compuesta por pares punteados, es la lista de asociaciones de una entidad linea que se almacena en la base de datos de AutoCAD. En dicha lista de asociaciones, el cdigo 0 nos indica el tipo de entidad. De modo que: (assoc 0 lista_de_entidad) devolver (0 . "LINE") (cdr (assoc 0 lista_de_entidad)) devolver "LINE" El cdigo 8 nos indica la capa en la que est la entidad, en este caso en la capa 0. El cdigo 10 indica el punto inicial de la lnea y el 11 el punto final. Por eso son tan importantes las listas de asociaciones y los pares punteados. Aplicar funciones a los elementos de las listas Supongamos que tenemos una lista de puntos: (setq lstptos (list (list 0.0 0.0) (list 10.0 0.0) (list 10.0 10.0) (list 0.0 10.0))) Es decir, lstptos es una lista con cuatro elementos correspondientes a los vrtices de un cuadrado de lado 10. Supongamos que queremos dibujar un crculo en cada uno de los puntos de lstptos. Hasta ahora podiamos hacerlo as: (setq i 0 nent (length lstptos)) (while (< i nent) (setq pt (nth i lstptos)) (command "_.circle" pt 1.0) (setq i (1+ i)) ) La variable i nos servir como ndice o contador en el bucle While, por eso le asignamos inicialmente el valor 0, porque los elementos de una lista se numeran empezando desde cero. Es bastante habitual que los

nombres de las variables utilizadas como contadores sean i j k ... Tambin asignamos a nent el nmero de elementos de la lista lstptos, porque sino no sabremos como salir del bucle ni hasta que elemento llegar. El bucle se ejecutar mientras el contador sea menor que nent es decir, se ejecutar para cada elemento de lstptos. (setq pt (nth i lstptos)) se obtiene el elemento nmero i de lstptos, que es un punto, y se dibuja un crculo de radio 1.0 en l. Finalmente aumentamos el contador para que pase al siguiente punto (setq i (1+ i)). Esta ltima lnea es muy importante, si estuviera i sera siempre igual a cero y el bucle no tendr fin. Por lo que dibujara infinitos crculos en el primer punto de lstptos y da un error. Veamos otro mtodo para dibujar los crculos en los vrtices de lstptos: (while lstptos (setq pt (car lstptos)) (command "_.circle" pt 1.0) (setq lstptos (cdr lstptos)) ) En este caso, el bucle se ejecutar mientras tengamos puntos en la lista es decir, mientras lstptos sea distinto de nil. Se define pt como el primer punto de la lista lstptos, se dibuja un crculo de radio 1.0 con centro en pt y por ltimo se mueve el contador. Pero, qu contador? Si no existe! Pues, (setq lstptos (cdr lstptos)) eliminamos el primer elemento de la lista lstptos. De modo que lstptos va perdiendo un elemento (punto) cada vez que se ejecuta el bucle. Llega un momento en el que lstptos = ((0.0 10.0)) y al evaluarse (setq lstptos (cdr lstptos)) se asignar a lstptos el valor devuelto por CDR, es decir nil. Con lo cual deja de ejecutarse el bucle. El inconveniente de este segundo mtodo es que se vara lstptos, que finalmente valdr nil. Por lo que la lista de puntos inicial se perder. Por eso solo se utilizar este segundo mtodo si no importa perder la lista lstptos. Bien, pero tenemos algunos mtodos que son mejores, y eso es lo que vamos a ver en este tema. (FOREACH variable lista expresion) Esta funcin te permite aplicar una expresin para cada elemento de la lista que recibe como argumento. La variable simboliza a un elemento de dicha list, por lo que se suele utilizar en la expresin. Por ejemplo: (foreach p lstptos (command "_.circle" p 1.0)) La lnea de cdigo anterio representa el tercer mtodo para dibujar los crculos en los puntos de lstptos. A la variable la llamamos p y la expresin a ejecutar para cada elemento de la lista es: (command "_.circle" p 1.0) dnde p es un elemento de la lista. En caso de tener una lista de cadenas de texto, podria hacerse lo siguiente: (setq lsttxt (list "Curso" "de" "AutoLISP")) (foreach p lsttxt (prompt p)) Con lo cual, escribira todos los textos en la ventana de comandos de AutoCAD. Se ha utilizado el mismo nombre de variable (p), pero podria haberse cambiado: (foreach pepe lsttxt (princ pepe)) Supongamos ahora que tenemos una lista de nmeros:

(setq lst (list 5.0 -1.5 2.6 3.8 9.7)) Para sumar los nmeros podriamos hacer lo siguiente: (setq i 0 nent (length lst) total 0.0) (while (< i nent) (setq num (nth i lst)) (setq total (+ total num)) (setq i (1+ i)) ) Inicialmente definimos la variable total como 0.0, el bucle se ejecutar para cada elemento de lst y se define num como el nmero que est en la posicin i sumamos a total ese nmero y movemos el contador. Cuando se entra por primera vez en el bucle, num = 5.0 de modo que total = 0.0 + 5.0. Al volver a entrar en el bucle num = -1.5 de modo que total = 5.0 + (-1.5) y as hasta que se llega al ltimo elemento de lst. Tambin podria hacerse sin emplear contadores, como hicimos antes. Y tambin podemos hacerlo con FOREACH, veamoslo: (setq total 0.0) (foreach p lst (setq total (+ total p))) Todo lo que antes poniamos en un bucle se pone ahora en una sola lnea de codigo. (APPLY nombre_funcin lista) Aplica la funcin que recibe como argumento a todos los elementos de la lista. El nombre de la funcin a utilizar en APPLY se indicar precedida por el smbolo ' para que AutoCAD interprete que le estamos pasando ese texto tal cual. El ejemplo anterior sera: (setq total (apply '+ lst)) En (apply '+ lst) estamos aplicando la funcin + a todos los elemento de lst, equivale a (+ 5.0 -1.5 2.6 3.8 9.7), y el resultado se lo asignamos a la variable total. Digamos que '+ es el nombre que utilizamos en AutoLISP para hacer referencia a la funcin +, ya que + es la funcin propiamente y no su nombre. Prueba lo siguiente en la ventana de comando de AutoCAD: (list "pepe" + 5.0) y fijate en lo que devuelve ("pepe" #<SUBR @0244f5a8 +> 5.0) Y ahora prueba con el literal, es decir con el caracter ' delante del + (list "pepe" '+ 5.0) devuelve ("pepe" + 5.0) Entiendes ahora porque ponemos '+? Para que no evalue la funcin, ya que devolveria #<SUBR @0244f5a8 y nos dara un error. Veamos un par de ejemplos ms: (apply 'max lst) devolver el mayor nmero de lst

(apply 'min lst) devolver el menor nmero de lst Si tuvieramos una lista cuyos elementos fueran textos: (setq lsttxt (list "Curso" "de" "AutoLISP")) para unir los textos: (setq txt (apply 'strcat lsttxt)) De modo que txt = "CursodeAutoLISP" todo junto. (MAPCAR nombre_funcin lista1 [lista2]...) Esta funcin aplica la funcin de AutoLISP que recibe como argumento a los elementos de las listas. Como as no se va a entender... ejemplos: Supongamos que tenemos dos puntos: (setq pt1 (getpoint "\nPunto 1: ")) (setq pt2 (getpoint pt1 "\nPunto 2: ")) Los puntos son listas con 2 o 3 elementos, en funcin de si tienen 2 o 3 coordenadas. Vamos a determinar el vector con origen en pt1 y final en pt2. Tenemos que calcular las coordenadas X Y Z del vector, que son el resultado de restar las respectivas coordenadas X Y Z de pt1 a las de pt2. Podemos obtener el vector as: (setq vector (list (- (car pt2) (car pt1)) (- (cadr pt2) (cadr pt1)) (- (caddr pt2) (caddr pt1)) ) ) Siendo la coordenada X del vector: (- (car pt2) (car pt1)) la Y (- (cadr pt2) (cadr pt1)) y la Z (- (caddr pt2) (caddr pt1)) Bien, pues esto mismo lo podemos hacer con MAPCAR (setq vector (mapcar '- pt2 pt1)) La funcin que recibe mapcar es - y lo que hace aplicarla a los elementos de pt2 y pt1, de modo que resta sus coordenadas X, las Y y las Z. APPLY devuelve un elemento y MAPCAR una lista. Si tenemos: Vamos a complicar un poco el tema... si quisieramos obtener el mdulo de un vector tendriamos que calcular la raz cuadrada de la suma de sus coordenadas al cuadrado.

(mapcar '* vector vector) devolver una lista con las coordenadas del vector al cuadrado, ya que las estamos multiplicando por si mismas. Para sumarlas utilizamos APPLY: (apply '+ (mapcar '* vector vector)) Para terminar solo nos falta hacer la raiz cuadrada de lo anterior: (setq modulo (sqrt (apply '+ (mapcar '* vector vector)))) Veamos otro ejemplo en el que se combinan APPLY y MAPCAR. Sean v1 y v2 dos vectores vamos a calcular su producto escalar. (setq v1 (list 10.0 10.0 0.0)) (setq v2 (list 5.0 0.0 0.0)) El producto escalar de los vectores v1 y v2 es la suma de los productos de las coordenadas de v1 por las de v2. Podriamos hacerlo as: (setq pescalar (+ (* (car v1) (car v2)) (* (cadr v1) (cadr v2)) (* (cadr v1) (cadr v2)) ) ) Pero resulta ms sencillo utilizando MAPCAR y APPLY. Primero multiplicamos las coordenadas de ambos vectores: (mapcar '* v1 v2) y luego las sumamos, de modo que... (setq pescalar (apply '+ (mapcar '* vec1 vec2))) (LAMBDA (lista_argumentos / variables_locales) expresin1 [expresin2] ...) Tal vez el formato de la funcin LAMBDA recuerde algo a DEFUN. LAMBDA tambin se utiliza para definir una funcin, pero a diferencia de DEFUN la funcin no se almacena en ningn lugar, en este caso es temporal. Por tanto solo se puede ejecutar donde se defina. Adems la funcin creada no tiene nombre, por lo que tampoco podriamos llamarla desde otra parte de nuestro cdigo. Utilizarla sola sin APPLY o MAPCAR no tiene sentido. Supongamos que queremos obtener el punto medio de dos puntos... (setq pt1 (getpoint "\nPunto 1: ")) (setq pt2 (getpoint pt1 "\nPunto 2: "))

Las coordenadas del punto medio sern las coordenadas de pt1 ms las de pt2 divididas por 2.0. Para sumar sus coordenadas... (mapcar '+ pt1 pt2) Bien... pero ahora, Cmo las dividimos por 2.0? Podemos hacer lo siguiente: (mapcar '/ (mapcar '+ pt1 pt2) (list 2.0 2.0 2.0)) Donde creamos una lista (list 2.0 2.0 2.0) y dividimos los elementos de (mapcar '+ pt1 pt2) entre los elementos de la lista anterior (2.0 2.0 2.0). Recordemos... (mapcar '+ pt1 pt2) devuelve la suma de las coordenadas de pt1 y pt2. Ahora necesitariamos dividila por 2.0, pues podemos crear una funcin que reciba un nmero y lo divida por 2.0 devolviendo el resultado: (defun 2/ ( num ) (/ num 2.0)) Y ahora pasarle a MAPCAR la nueva funcin 2/ para que divida cada elemento de (mapcar '+ pt1 pt2) por 2.0 (mapcar '2/ (mapcar '+ pt1 pt2)) Pero tambin podemos hacerlo de otra forma, utilizando la funcin LAMBDA en lugar de DEFUN. Primero veamos como sera nuestra funcin definida mediante LAMDA: (lambda ( num ) (/ num 2.0)) Es muy parecido a lo que hicimos con DEFUN. Pero la funcin definida mediante LAMBDA no tiene nombre y no se almacena en ningn sitio, es temporal, de modo que no podemos llamarla. Dnde debemos utilizar LAMBDA? Pues directamente en MAPCAR, donde hay que indicar el nombre de la funcin: (mapcar '(lambda ( num ) (/ num 2.0)) (mapcar '+ pt1 pt2)) Por tanto, cuando queremos aplicar APPLY o MAPCAR a una o varias listas y ejecutar una funcin que no existe en AutoLISP, podemos crearla previamente con DEFUN o utilizar la funcin LAMBDA para definirla in situ. (VER) Esta funcin devuelve una cadena de texto con la versin de AutoLISP que se est ejecutando. Por ejemplo: "Visual LISP 2000 (es)". Entre parntesis indica la versin idiomtica, en este caso Espaol. Pantalla de texto y pantalla grfica En AutoCAD podemos pasar de pantalla grfica a la pantalla de texto, y al revs, pulsando la tecla de funcin F2. En AutoLISP tambin existen algunas funciones para realizarlo directamente desde el cdigo de nuestras rutinas. (TEXTSCR) Se utiliza para pasar a pantalla de texto y siempre devuelve nil. Se suele emplear cuando se quiere mostrar mucha informacin en pantalla de texto. (GRAPHSCR) Pasa a pantalla grfica y tambin devuelve nil. Se utiliza para asegurarnos que el usuario est viendo la pantalla grfica, por ejemplo para indicar un punto. Especialmente se utilizar si antes se ha pasado a pantalla de texto.

(TEXTPAGE) Esta funcin es anloga a TEXTSCR. Pasa a pantalla de texto y tambin devuelve nil. Tal vez estes preguntandote... Cuntas funciones nos quedan an? Pues entre otras cosas, la siguiente funcin nos servir para ver las funciones de AutoLISP que hemos visto y las que nos quedan. (ATOMS-FAMILY formato [lista_simbolos]) Esta funcin devuelve una lista con los smbolos que se han definido en el dibujo actual. Qu es un smbolo? Pues un nombre de variable de AutoLISP, el nombre de una funcin de usuario que hallamos creado y tambin todos los nombres de las funciones propias de AutoLISP. El argumento formato puede tener dos valores:

0 para que devuelva una lista con los nombres de los smbolos. 1 para que devuelva una lista, pero siendo sus elementos cadenas de texto.

Veamos ahora algn ejemplo. Al escribir la lnea siguiente sabras cuantas funciones faltan... (atoms-family 0) esto mostrar una lista con los nombres de todos los smbolos definidos en el dibujo actual. (atoms-family 1) as los elementos de la lista anterior sern cadenas de texto. En las listas anteriores es dificil encontrar algo. Recuerdas la funcin acad_strlsort? Permita organizar alfabticamente una lista de cadenas de texto. (acad_strlsort (atoms-family 1)) devolver la lista anterior ordenada Estan todas las funciones de AutoLISP, pero hay otras muchas funciones que aparecen en la lista y no son funciones de AutoLISP. As que no te asustes, que no son tantas. Recordemos el formato de esta funcin: (ATOMS-FAMILY formato [lista_simbolos]) y veamos que es eso de la lista de smbolos... Para saber si unas funciones determinadas existen, es decir si estn definidas, creamos una lista con sus nombres y se lo pasamos como argumento a atoms-family (atoms-family 1 (list "car" "cdr")) devolver una lista con sus nombres ("CAR" "CDR") Aunque lo habitual no es emplear esta funcin para detectar si estn definidas las funciones de AutoLISP, sino para detectar nuestras propias funciones y variables: (atoms-family 1 (list "car" "cdr" "variable")) devuelve ("CAR" "CDR" nil) ya que el smbolo "variable" no tiene asociado ninguna funcin ni variable de AutoLISP. Si definimos: (setq variable 12.5) (atoms-family 1 (list "car" "cdr" "variable")) devolver ("CAR" "CDR" "VARIABLE") devuelve el nombre de la variable, no su valor. Si definimos una funcin de usuario: (defun 2+ ( numero / ) (+ numero 2.0))

La funcin 2+ recibe un nmero y le suma 2.0. (atoms-family 1 (list "car" "cdr" "2+")) devolver ("CAR" "CDR" "2+") (QUOTE expresin) Esta funcin recibe una expresin y devuelve su literal, es decir devuelve la expresin tal cual, sin evaluar. (quote +) devolver + Esto es lo mismo que haciamos en APPLY y MAPCAR: (apply '+ (list 2.0 3.5 6.8)) pues el apstrofe ' es el diminutivo o el alias de la funcin QUOTE. (quote (setq a "texto" b 10.0)) devolver (SETQ A "texto" B 10.0) sera lo mismo escribir: '(setq a "texto" b 10.0) Pero no podremos escribir esta ltima lnea en la ventana de comandos de AutoCAD, por que el interprete de comandos no detecta el parntesis en primer lugar y piensa que no es una expresin de AutoLISP sino un comando de AutoCAD. Podemos utilizar un truco para evaluarla desde la ventana de comandos: (progn '(setq a "texto" b 10.0)) PROGN en realidad no hace nada, simplemente nos serva para salvar la limitacin de IF de indicar ms de 1 expresin, ya que evala las expresiones que contiene y devuelve el resultado de la ltima expresin evaluada. Ahora si devolver (SETQ A "pepe" B 10.0). Pero no hemos asignado valores a las variables, ya que (SETQ A "pepe" B 10.0) no se ha evaluado. (quote (15.0 10.6 9.2)) devolver (15.0 10.6 9.2) es decir, devuelve una lista. Tambin funcionara con '(15.0 10.6 9.2) Por tanto en lugar de: (apply '+ (list 2.0 3.5 6.8)) podemos poner: (apply '+ (quote (2.0 3.5 6.8))) y tambin: (apply '+ '(2.0 3.5 6.8)) De modo que podemos usar QUOTE y ' para crear listas. Pero con excepciones... si hacemos: (setq a 2.0) (apply '+ '(a 3.5 6.8)) indicar: ; error: tipo de argumento errneo: numberp: A a es el nombre de una variable, es un smbolo, cuya variable tiene asociado el valor 2.0

(apply '+ (list a 3.5 6.8)) esto si funcionar porque (list a 3.5 6.8) devolver (2.0 3.5 6.8), LIST se evala pero QUOTE no evalua la expresin que recibe. Resumiendo: Podemos crear listas con QUOTE o con ' pero siempre que conozcamos los elementos de dichas listas, y que estos sean valores concretos no determinados a partir de expresiones o almacenados en variables. Algunos ejemplos ms: (setq maximo (apply (quote max) (quote (10.5 15.2 9.3)))) (setq minimo (apply 'min '(10.5 15.2 9.3))) (foreach p '((0.0 0.0 0.0) (10.0 10.0 0.0)) (command "_.circle" p 1.0)) (setq vector (mapcar '- '(10.0 10.0 0.0) '(5.0 0.0 0.0))) (setq ptomed (mapcar '(lambda ( num ) (/ num 2.0)) (mapcar '+ '(10.0 10.0 0.0) '(5.0 0.0 0.0)))) (EVAL expresin) Esta funcin evala la expresin que recibe. (eval 2.5) devuelve el resultado de evaluar 2.5, es decir devuelve 2.5 (eval "Soy una cadena de texto") analogamente devolver "Soy una cadena de texto" Pero veamos un ejemplo algo ms complicado: (setq a 15.5) (setq b (quote a)) Qu valor tendr asignado b? (quote a) es el nombre de la variable a, es decir el smbolo. As que b tendr asociado el nombre de la variable a. (+ b 10.0) dar un error (eval b) devolver 15.5 de modo que podemos hacer lo siguiente: (+ (eval b) 10.0) devolver 25.5 (eval '(+ 10.0 5.5)) devolver 15.5 ya que '(+ 10.0 5.5) devuelve (+ 10.0 5.5) y eval, lo evala. (READ texto) Esta funcin lee el texto que recibe y devuelve la primera palabra. (read "AutoLISP") devuelve "AutoLISP" (read "Curso de AutoLISP") devolver "Curso" (read "(15.2 9.3 15.5)") en este caso devolver (15.2 9.3 15.5) porque al detectar el parntesis lo considera un mismo trmino. Es algo similar a escribir en la ventana de comandos de AutoCAD, si no se pone un parntesis delante no dejar escribir espacios en blanco. Pues este es otro mtodo para crear una lista, por tanto:

(apply 'max (read "(15.2 9.3 15.5)")) devolver el mximo de la lista de nmeros indicados Y que pasa si hacemos: (setq txt "(setq a 5.5)") (read txt) devolver (setq a 5.5) y ahora podemos evaluarlo con EVAL (eval (read txt)) devolver 5.5 y asigna a la variable a el valor 5.5 Para qu sirve esto? Para solicitarle al usuario una expresin de AutoLISP y evaluarla: (setq txt (getstring T "\nExpresin de AutoLISP: ")) (setq valor (eval (read txt))) En este caso asignamos a valor el resultado de evaluar una expresin de AutoLISP introducida por el usuario. (SET literal_de_smbolo expresin) Esta funcin es muy parecida a SETQ, se diferencia en que espera el literal de un smbolo. Veamoslo con ejemplos: (setq num1 5.0) asigna a la variable num1 5.0 (set num2 5.5) da un error, ya que espera un literal (set 'num2 5.5) asigna a la variable num2 el valor 5.5 Si probamos: (setq (read "num1") 5.0) nos da un error, ya que SETQ no acepta una expresin como smbolo. Sin embargo SET si lo acepta: (set (read "num2") 15.0) asigna a la variable num2 el valor 15.0 Carga automtica de los archivos de AutoLISP AutoCAD carga automticamente dos archivos de AutoLISP, si es que existen y se encuantran en los directorios de soporte. Se trata del ACAD.LSP y el ACADDOC.LSP. El archivo ACAD.LSP se carga al iniciar AutoCAD y el ACADDOC.LSP se carga siempre que se abre un dibujo, o se crea un dibujo nuevo. De modo que las funciones cotenidas en el ACAD.LSP tan solo estarn cargadas en el dibujo que se abre al iniciar AutoCAD, mientras que las funciones contenidas en el ACADDOC.LSP estarn cargadas en todos los dibujos. Lo lgico ser entonces guardar nuestras funciones en el ACADDOC.LSP y no en el ACAD.LSP. El ACAD.LSP se utilizar tan solo para almacenar aquellas funciones que nos interese ejecutar al cargar AutoCAD. Por ejemplo, podemos utilizar el archivo ACAD.LSP para mostrar una imagen, foto, al iniciar AutoCAD. Si tenemos alguna rutina que se utilice mucho, o que se suele ejecutar en todos los dibujos la meteremos en el ACADDOC.LSP

Qu riutinas meteremos en el ACADDOC.LSP? Conviene que no sean demasiadas, para que dicho archivo sea ms manejable y adems para no ocupar demasiado espacio en la memoria del ordenador. Por tanto, solo incluiremos en el ACADDOC.LSP las rutinas ms empleadas. Por ejemplo una funcin de tratamiento de errores, o las funciones GAR y RAG que se utilizan bastante, y adems son bastante pequeas. Qu pasa si metemos tambin nuestra funcin C:CIRCPERI? Pues no pasara nada pero... Vas a dibujar crculos dado su permetro en "todos" los dibujos? no creo, por eso no merece la pena aadirla al ACADDOC.LSP, ya que si hacemos lo mismo con todas nuestras rutinas... el archivo ACADDOC.LSP tendria un tamao descomunal. En cambio, podemos guargar la rutina CIRCPERI en un archivo independiente y cargarlo desde el ACADDOC.LSP. En el caso de la rutina CIRCPER, podemos guardarla en el archivo CIRCPERI.LSP, dentro de uno de los directorios de soporte de AutoCAD, e incluir la siguiente lnea en el ACADDOC.LSP: (load "circperi.lsp" (alert "Archivo Circperi.lsp no encontrado")) De este modo tenemos la rutina CIRCPERI en un archivo independiente, lo que nos soluciona parte del problema, ya que tendremos el archivo ACADDOC.LSP ser bastante corto y si queremos modificar la rutina CIRCPERI tan solo tenemos que manipular un archivo en el que nicamente est definida dicha funcin. Pero seguimos teniendo algunos problemas... Si nuestra coleccin de rutinas es muy extensa tenemos que aadir una lnea de cdigo como la anterior para cada rutina, lo que se traducir en un archivo ACADDOC.LSP bastante grande y dificil de manipular. Adems, el mayor inconveniente es que todas nuestras rutinas se cargarian en memoria automticamente para cada archivo de dibujo, con lo que estaremos sobrecargando la memoria del ordenador. (AUTOLOAD archivo lista_comandos) La funcin AUTOLOAD es similar a LOAD, tambin nos permite cargar archivos de AutoLISP pero solo aquellos archivos que contengan comandos, por ejemplo C:CIRCPERI pero no servir para archivos en los que solo tengamos funciones como GAR y RAG. Qu ventaja tiene el utilizar AUTOLOAD en lugar de LOAD? Al utilizar AUTOLOAD los archivos no se cargan automticamente, simplemente se predispone a AutoCAD para que los cargue en cuanto se ejecute uno de los comandos indicados como argumentos en la lista de comandos. Por ejemplo, si tenemos un archivo con 3 comandos: (defun C:comando1 ( / ) ....) (defun C:comando2 ( / ) ....) (defun nosoyuncomando ( / ) ....) (defun C:comando3 ( / ) ....) En el archivo ACADDOC.LSP podriamos aadir la siguiente lnea: (autoload "archivo.lsp" '("comando1" "comando2")) Al iniciar un dibujo, se carga el ACADDOC.LSP y se evala la lnea anterior, pero archivo.lsp no se cargar hasta que se ejecute el comando1 o el comando2. Sin emargo no se cargar si ejecutamos el comando3, ya que no lo hemos incluido en la lista de comandos. Al ejecutar uno de los dos comandos anteriores se carga el archivo, de modo que la funcin nosoyuncomando tambin se cargar, al igual que el comando3. Y qu pasa con los archivos de AutoLISP que no tienen comandos, es decir en los que solo hay funciones? Pues tendremos que cargarlos con LOAD, con lo cual se cargarian todos directamente en la memoria y si tenemos bastantes funciones seguimos teniendo el mismo problema que antes. Cmo podemos solucionarlo? Pues cargando las funciones solo cuando hacen falta. Por ejemplo, supongamos que en el comando C:CIRPERI se utiliza la funcin RAG... tenemos dos opciones:

Incluir la funcin RAG en el archivo Circperi.lsp de este modo al cargarse el archivo se cargar la funcin RAG. Esto se suele hacer para las funciones que solo se utilizan en un comando determinado. Pero si la funcin RAG deseamos utilizarla en otros comandos, es mejor utilizar el siguiente mtodo... Incluir la funcin RAG en un archivo independiente Rag.lsp y aadir la siguiente lnea de cdigo dentro del archivo Circperi.lsp: (load "Rag.lsp" (alert "Archivo Rag.lsp no encontrado")) De este modo, el archivo Circperi.lsp se cargar cuando se ejecute uno de los comandos indicados en la funcin Autoload del archivo ACADDOC.LSP. Y al cargarse el archivo Circperi.lsp se cargar mediante la funcin Load el archivo Rag.lsp. Utilizando este mtodo podemos utilizar una misma funcin en multiples rutinas, tan solo debemos asegurarnos de que dicha funcin est definida, es decir que se halla cargado el archivo en el que se encuentre. Los archivos ACAD.LSP y ACADDOC.LSP no tienen porque existir. Si no existen pueden crearse, teniendo en cuenta que se deben guardar en uno de los directorios de soporte de AutoCAD. En caso de que ya existan, se pueden editar para incluir el cdigo deseado. Conviene hacer una copia de seguridad de estos archivos porque muchos programadores crean sus propios archivos ACAD.LSP y ACADDOC.LSP de modo que al instalar un mdulo o aplicacin para AutoCAD, podeis sobreescribir vuestros archivos. Existen otros dos mtodos para cargar automticamente rutinas de AutoLISP. El primero consiste en utilizar la opcin disponible en el men de AutoCAD "Herr --> AutoLISP --> Cargar" ya que en el letrero de dialogo que aparece se pueden cargar rutinas y se pueden seleccionar las rutinas que se desean cargan al inicio. El tercer mtodo para cargar automticamente las rutinas de AutoLISP es editando los mens de AutoCAD, pero esto lo veremos ms adelante... Ejecucin automtica de cdigo de AutoLISP Hemos visto que el archivo ACADDOC.LSP se carga automticamente en todos los dibujos pero... Cmo podemos ejecutar automticamente una funcin al iniciarse un dibujo? Supongamos que tenemos definida la funcin GAR dentro del ACADDOC.LSP: (defun GAR ...) Para ejecutarla, tan solo debemos aadir en dicho archivo la siguiente lnea de cdigo: (GAR) En caso de que dicha funcin tenga argumentos, estos deben indicarse: (GAR 3.141592) Cmo ejecutariamos automaticamente el comando C:CIRCPERI? Una vez cargado el comando, para ejecutarlo desde la ventana de comandos de AutoCAD podemos escribir directamente su nombre CIRCPERI para ejecutarlo. Sin embargo, desde un archivo de AutoLISP no podemos ejecutarlo as. Tenemos que hacerlo de esta otra forma: (C:CIRCPERI) De modo que podriamos incluir en el archivo ACADDOC.LSP las dos siguientes lneas de cdigo: (load "circperi.lsp" (alert "Archivo Circperi.lsp no encontrado")) ; Carga el comando (C:CIRCPERI) ; Ejecuta el comando

Sera mejor utilizar AUTOLOAD en lugar que LOAD en el ejemplo anterior? Pues no. Utilizar AUTOLOAD no tendra mucho sentido, puesto que al ejecutar el comando el archivo en el que est definido se cargara, por tanto podemos cargarlo ya antes. Existe otro mtodo para ejecutar cdigo directamente. Se trata de escribirlo directamente, es decir sin meterlo dentro de ninguna funcin o comando. Por ejemplo, si incluimos las siguientes lneas de cdigo en el archivo ACADDOC.LSP nos permitir dibujar un circulo dado su centro y su permetro: (setq pto (getpoint "\nCentro del crculo: ")) (setq peri (getdist pto "\nPermetro: ")) (command "_.circle" pto (/ peri (* 2.0 PI))) Pero en este caso, el cdigo anterior solo se ejecuta una vez. Si queremos dibujar otro crculo dado su permetro, no tendriamos la funcin CIRCPERI definida. Para qu se suele utilizar este mtodo? Por ejemplo, para mostrar mensajes informativos o de bienvenida: (alert "Estas seguro de que quieres utilizar AutoCAD?") (alert "Estas realmente seguro?") (alert "Seguro, seguro?") Aunque no os aconsejo que utilieis este ejemplo, os cansariais de l a los 5 minutos. Adems de mensajes de bienvenida podeis hacer otras cosas ms tiles, como asignar valores iniciales a algunas variables: (setq directorio "c:\\rutinas\\") Tambin podeis modificar el valor de alguna variable de sistema de AutoCAD: (setvar "osmode" 7) S::STARTUP Existe otro mtodo para ejecutar cdigo automticamente. Hemos visto que AutoCAD utiliza una funcin de tratamiento de errores por defecto que se llamaba *error* y tambin hemos visto que podiamos redefinirla creando nuesta propia funcin de tratamiento de errores. Bien, pues AutoCAD tambin tiene una funcin interna que se ejecuta automticamente se trata de S::STARTUP. El que las funciones *error* y S::STARTUP tengan estos nombres tan raros es para evitar que a alguien se le ocurra denominar as a alguna funcin de otro tipo. Para definir la funcin S::STARTUP se utiliza DEFUN, al igual que para cualquier otra funcin. (defun S::STARTUP ( / ) (setq directorio "c:\\rutinas\\") ) La funcin S::STARTUP podemos utilizarla para los mismos ejemplos que se daban antes.

Por ltimo, para terminar el tema, un consejo... Al programar hay que tener bastante cuidado para no cometer errores, ya que el omitir un simple parntesis o unas comillas, por ejemplo, modificaran totalmente nuestras funciones. Cuando adems estas funciones se van a ejecutar automticamente, como en los ejemplos que se han expuesto, la precaucin al programar debe ser mxima. Operaciones con archivos Antes de comenzar con el tema de lectura y escritura de archivos, veamos una pequea rutina... CARGALISP CARGALISP recibir una lista como la siguiente: (("c:\\rutinas\\lisp1.lsp" ("gar" "rag")) ("c:\\rutinas\\lisp2.lsp" ("inverso" "tg"))) En la que cada elemento es a su vez otra lista formada por el nombre de un archivo de AutoLISP (con su ruta, si es necesario) y una lista con los nombres de las funciones (o variables globales) definidas en dicho archivo. Por ejemplo, en el caso anterior se supone que en el archivo "c:\\rutinas\\lisp1.lsp" estn definidas las funciones "gar" y "rag" y que en el archivo "c:\\rutinas\\lisp2.lsp" estn definidas las funciones "inverso" y "tg". CARGALISP cargar los archivos de AutoLISP indicados si no estaban definidas, o cargadas que es lo mismo, las funciones indicadas para cada archivo. (defun CARGALISP ( lst / ) (while lst (if (not (apply 'and (atoms-family 1 (cdar lst)))) (progn (load (caar lst)) (prompt (strcat "\nCargando archivo " (caar lst) "...")) ) ) (setq lst (cdr lst)) ) ) Tal como est funciona perfectamente. Pero... puede producirse un pequeo error. Qu pasa si no existe, o no se encuentra, el archivo lisp1.lsp o cualquiera de los archivos de la lista que reciba CARGALISP? (FINDFILE archivo) Esta funcin nos permite comprobar la existencia de un archivo. Si el archivo existe devuelve su nombre y si no existe, o no lo encuentra en la ruta indicada, devolver nil. En caso de que no se indique la ruta en la que debe buscar el archivo, lo buscar en los directorios de soporte de AutoCAD. (findfile "c:\\autoexec.bat") debera devolver "c:\\autoexec.bat", supongo que vuestro PC tiene Autoexec.bat, claro... (findfile "c:\\noexisto.bat") debera devolver nil

FINDFILE en realidad no hace nada, tan solo se utiliza para comprobar la existencia de un archivo. Entonces,...Cuando se utilizar FINDFILE? Pues os voy a poner tres ejemplos en los que se suele utilizar: 1. 2. 3. 4. Antes Antes Antes Antes de de de de cargar un archivo con LOAD o AUTOLOAD abrir un fichero de texto, ya sea para leerlo o escribir en l. "abrir" un archivo de dibujo en AutoCAD. cargar y utilizar un archivo DCL (Letrero de dialogo) en AutoLISP.

Por tanto, deberiamos modificar el cdigo de la funcin CARGALISP para comprobar la existencia de los archivos a cargar. (defun CARGALISP ( lst / ) (while lst (if (not (apply 'and (atoms-family 1 (cdar lst)))) ;; Comprueba si todas las funciones del primer elemento de la lista ;; estn cargadas (if (not (findfile (caar lst))) (alert (strcat "No se ha encontrado el archivo " (caar lst))) ;; No se encuentra el archivo (progn (load (caar lst)) ;; Carga el archivo (prompt (strcat "\nCargando archivo " (caar lst) "...")) ;; Indica que se ha cargado el archivo ) ;; Se encuentra el archivo ) ) (setq lst (cdr lst)) ;; Pasa al siguiente elemento de la lista ) ;; Para cada elemento de la lista ) (GETFILED titulo_letrero archivo_defecto extensin_defecto modo)

Esta funcin nos permitir seleccionar un archivo, ya veremos ms adelante como seleccionar varios archivos. GETFILED muestra un letrero de gestin de archivos tipico de Windows. Podemos pasarle a GETFILED el ttulo del letrero, el archivo seleccionado por defecto y la extensin de archivos que se buscar por defecto. GETFILED devuelve el nombre y ruta del archivo seleccionado. El argumento modo es de tipo binario y ofrece los siguientes valores:

1 --> Para designar un archivo nuevo, es decir, que no exista. 2 --> Desactiva la casilla "Teclearlo". 4 --> Permite indicar cualquier extensin. 8 --> Devuelve el nombre del archivo, sin la ruta

(getfiled "Selecciona un archivo" "c:\\autoexec.bat" "bat" 0) No pasa nada al pulsar en Abrir, ya que no se abre el archivo. GETFILED tan solo nos deja seleccionarlo y devuelve su nombre. (getfiled "Selecciona un archivo nuevo" "" "" 1) Lectura y escritura de archivos de texto AutoLISP nos permite leer y escribir archivos de texto ASCII. Al utilizar archivos de texto en nuestras rutinas debemos siempre seguir los siguientes pasos: 1. 2. 3. 4. Comprobar si existe el archivo. Abrir el archivo. Leer o escribir en el archivo. Cerrar el archivo.

(OPEN archivo modo) Esta funcin nos permite abrir un archivo de texto y devuelve un "descriptor de archivo". Qu es un descriptor de archivo? Es algo similar a un canal por el que se comunica AutoCAD con dicho archivo. Podemos abrir varios archivos, es decir abrir varios canales de comunicacin, de modo que al leer o escribir en un archivo, tenemos que saber cual es el canal de comunicacin por el que vamos a recibir o enviar datos, es decir necesitamos conocer el descriptor del archivo. Hay tres formas de abrir un archivo de texto, en funcin de lo que se quiera hacer a continuacin con dicho archivo. El argumento modo es el encargado de diferenciar estas tres formas distintas de abrir archivos:

Podemos abrir un archivo para leerlo --> modo "r" Podemos abrir un archivo de texto para escribir en l comenzando desde la primera lnea --> modo "w" Podemos abrir un archivo de texto para escribir en l a partir de la ltima lnea --> modo "a"

Para abrir el archivo autoexec.bat en modo lectura: (open "c:\\autoexec.bat" "r") Devuelve #<file "c:\\autoexec.bat"> es decir, el descriptor del archivo. Aunque en la lnea anterior se abre el archivo autoexec.bat en modo lectura, luego no podriamos hacer nada con l ya que no hemos guardado el descriptor de archivo. Es decir, no podemos decirle a AutoCAD que lea texto por el canal de comunicacin que hemos abierto, ya que no conocemos el descriptor del archivo. Cmo se utiliza entonces la funcin OPEN?

(setq darch (open "c:\\autoexec.bat" "r")) De este modo el valor devuelto por OPEN, el descriptor de archivo, se almacena en la variable darch. Antes de ver como leer o escribir en los archivos de texto, veamos como tendriamos que cerrarlos... (CLOSE descriptor_archivo) Cierra el archivo cuyo descriptor se indica y devuelve nil. Fijate en lo importante que es guardar el descriptor del archivo en una variable, ya que si no lo hacemos no solo no podremos leer o escribir en el archivo, tampoco podremos cerrarlo. Hay que tener una cosa en cuenta al trabajar con archivos de texto: Al abrir un archivo de texto debemos indicar el modo (lectura, escritura, o aditivo) de modo que debemos saber de antemano lo que vamos a hacer con el archivo, y SOLO podremos hacer una cosa o leer o escribir. Aunque podemos abrir un archivo en modo escritura, escribir en l, cerrarlo, abrirlo en modo lectura, leer y volver a cerrarlo. Otra cuestin de especial interes es que si abrimos un archivo de texto existente en modo escritura "w", nos habremos cargado todo lo que tena anteriormente dicho archivo. As que mucho cuidado con los archivos que se abren en modo escritura. Escritura de archivos de texto Para crear un archivo de texto debemos abrirlo en modo escritura: (setq darch (open "c:\\nuevo.txt" "w")) Devuelve #<file "c:\\nuevo.txt"> el descriptor del archivo abierto, que se almacena en la variable darch. Tambin podemos abrir el archivo en modo aditivo, para continuar escribiendo a partir de la ltima lnea de texto del archivo. Bien, ya tenemos abierto nuestro archivo de texto, vamos a escribir en l: (PRIN1 [expresin [descriptor_archivo]]) Esta funcin escribe una expresin en el archivo cuyo descriptor se indique. Si no se indica el descriptor de archivo, la expresin se escribir en la ventana de comandos de AutoCAD. Veamos algunos ejemplos: (setq a 5.5 b "Curso de AutoLISP") (prin1 a) escribir el valor de la expresin a en la ventana de comandos de AutoCAD, es decir, escribir 5.5 Si ejecutas las dos lneas de cdigo anteriores desde la ventana de comandos de AutoCAD, veras que (prin1 a) parece devolver 5.55.5 en realidad devuelve 5.5 pero el eco de mensajes, repite el valor devuelto por la expresin (prin1 a) es decir 5.5 Por eso, aparece 5.55.5 (prin1 b) Escribir en la ventana de comandos "Curso de AutoLISP". Fijate que PRIN1 puede escribir tanto cadenas de texto como nmeros. Sin embargo la funcin PROMPT tan solo puede recibir como argumeto una cadena de texto. (prin1 'a) escribe a (prin1 (+ 15 5.5)) escribe 20.5

Hemos indicado una expresin como argumento, si utilizamos PROMPT en lugar de PRIN1 dar un error. (prin1 '(+ 15 5.5)) escribe (+ 15 5.5) Si no se indica la expresin a escribir, escribe una cadena nula (prin1) Por eso se suele emplear como ltima expresin de los comandos, para que la salida de nuestras rutinas sea limpia y no se vea el eco de mensajes de la ltima expresin evaluada. (prin1 (strcat "\t" b)) devuelve "\tCurso de AutoLISP" PRIN1 no entiende los caracteres de control como "\n" "\t" etc (PRINT [expresin [descriptor_archivo]]) Esta funcin es muy parecida a PRIN1, veamos en que se diferencian... (progn (prin1 a) (prin1 a)) escribe 5.55.5 (progn (print a) (print a)) escribe 5.5 pasa a otra lnea y escribe 5.5 Es decir, PRINT salta de lnea antes de escribir la expresin indicada. PRINT escribe la expresin en una lnea nueva. (print) devuelve una cadena nula, por eso tambin se emplea como ltima expresin de los comandos (print (strcat "\t" b)) devuelve "\tCurso de AutoLISP", es decir PRINT tampoco entiende los caracteres de control. (PRINC [expresin [descriptor_archivo]]) Esta funcin tambin es muy parecida a las anteriores. (progn (princ a) (princ a)) escribe 5.55.5 (princ) devuelve una cadena nula, por eso tambin se emplea como ltima expresin de los comandos (princ (strcat "\t" b)) ahora escribe "Curso de AutoLISP" pero tabulado. Por tanto, PRINC se diferencia de las anteriores en que si interpreta el significado de los caracteres de control. (WRITE-CHAR cdigo_ASCII [descriptor_archivo]) Esta funcin permite escribir un caracter, recibe como argumento su cdigo ASCII. Si se indica el descriptor de archivo lo escribir en el archivo y sino lo escribir en la ventana de comandos de AutoCAD. (write-char 65) escribe A. Es lo mismo que escribir (write-char (ascii "A")) (WRITE-LINE texto [descriptor_archivo]) Esta funcin escribe una lnea de texto entera. El primer argumento debe ser una cadena de texto no una expresin. Y si se indica un descriptor de archivo, escribir la lnea de texto en el archivo, en caso contrario la escribe en la ventana de comandos de AutoCAD.

(write-line "Curso de AutoLISP") Lectura de archivos de texto Para leer un archivo de texto debemos abrirlo en modo lectura: (setq darch (open "c:\\autoexec.bat" "r")) (READ-CHAR [descriptor_archivo]) Lee un caracter del archivo cuyo descriptor se indica. Si no se indica el descriptor, lo lee de la ventana de comandos de AutoCAD. (read-char) escribe un caracter y devolver su cdigo ASCII (READ-LINE [descriptor_archivo]) Esta funcin lee una lnea del archivo de texto indicado. Si no se indica el descriptor de archivo, la leera de la ventana de comandos de AutoCAD. (read-line) escribir una palabra o frase y devolver una cadena de texto con lo que has escrito

También podría gustarte