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:
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 en tono jocoso que su nombre proviene de Lost In Stupid
Parenthesis.
AutoLISP aade al LISP algunas funciones especialmente diseadas para la manipulacin
de las 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 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 de AutoLISP es la posibilidad de acceder a la base de
datos de los dibujos de AutoCAD. Donde podemos acceder a las capas, estilos de texto,
sistemas de coordenadas personales (SCP) as como a todas las entidades del dibujo. Esta
informacin se puede modificar, extraer e incluso aadir ms entidades al dibujo o informacin
adicional.
La mayora de los primeros lenguajes de programacin de alto nivel como C o Pascal son
lenguajes procedimentales, se ejecutan una serie de instrucciones paso a paso a partir de unos
datos. En AutoLISP en lugar de seguir instrucciones prefijadas, se puede acceder a la
informacin de los objetos de AutoCAD para determinar de qu 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 podramos 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 puede ser sin ninguna 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 macros en archivos de texto utilizando el entorno
de Visual LISP proporcionado por AutoCAD.
Existen dos inconvenientes al emplear AutoLISP desde la ventana de comandos de AutoCAD:
en 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. Recuerda que 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 intrprete 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 intrprete de AutoLISP para que la evale.
En el siguiente captulo veremos algunas de las operaciones matemticas que se pueden realizar
con AutoLISP.
Smbolos: Cualquier elemento que no sea un valor concreto. Por ejemplo una variable, una
funcin
Reales: Nmeros reales (180 es un nmero entero, pero 180.0 es un nmero real)
Ejemplo:
(+ 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, pues 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 y el cdigo a teclear se mostrarn en negrita.
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:
recuperar el valor inicial de las variables de sistema de AutoCAD que han sido modificadas
aadir unas lneas al final del programa para indicar el nombre del nuevo comando, autor,
etc.
Qu es una variable?
No sabes qu 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 yo z, y por qu no melones?
no utilizaremos nombres de variables que coincidan con los nombres de las funciones de
AutoLISP ni de las variables de sistema de AutoCAD
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.0respectivamente. 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 trmino ha sido empleado como un smbolo (nombre de variable o una
funcin de usuario) de AutoLISP. En cuyo caso devolver su valor y en caso contrario devolver 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 asignado a la
variable 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 caracteres de control:
\\ representa al smbolo \
\ representa al smbolo
(setq b T)
(setq c nil)
En este caso, a la variable a se le ha asignado una cadena de texto, a la variable b el valor Cierto o
Verdadero y a la variable c el valor Vaco o Falso.
Para almacenar el radio de la circunferencia de permetro 6 unidades en una variable podemos
teclear:
(setq rad (/ 6 (* 2 PI)))
Podramos crear una circunferencia e indicar !rad a AutoCAD cuando nos pregunte por el radio
deseado.
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 ahora 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.
(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
(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 Cuntos aos tienes?:)
Podemos asignar el valor introducido por el usuario a una variable y as utilizarlo despus.
(setq edad (getint Cuntos 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 Cuntos 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 Cuntos 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
intrprete 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
intrprete 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 Cuntos 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 Cuntos kilos pesas?:)
(setq peso (getreal mens))
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 y2. Las funciones de AutoLISP las ejecutamos escribiendo
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 pasar ningn argumento, o pasando un
argumento que no sea de tipo numrico?. Veamos:
(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.
Puedes comprobarlo escribiendo! Valor en la lnea de comandos, lo que nos devolver nil.
En la funcin anterior tenemos un argumento y no hay variables locales. La vamos a modificar un
poco:
(defun 2+ ( valor ) (setq valor (+ valor 2)))
Ahora seguro que estas totalmente convencido de que el resultado obtenido se almacena en la
variable valor. Pues comprobemos si tienes razn:
(2+ 5) devuelve 7
!valor devuelve nil
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.
dentro de la funcin 2+ asignamos (setq valor (+ valor 2)) de modo que valor ser 6
pero como se trata de una variable local, al salir de la funcin 2+ recuperamos el valor que
tena la variable valor antes de llamar a la funcin, de modo que valor ser 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 ser 6.
Retoquemos un poco ms la funcin 2+
(defun 2+ ( valor ) (setq inicial valor valor (+ valor 2)))
y borremos el contenido de valor. Para ello simplemente le asignamos nil.
(setq valor nil)
En este caso, dentro de la 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? Vamos a comprobarlo:
!valor devuelve nil tal como suceda en el ejemplo anterior
!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 se sigue empleando 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 debern aadir a la lista de variables locales dentro de las
definiciones de las funciones de usuario.
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
De modo que en la ventana de comandos de AutoCAD tan slo se escribirn 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 intrprete 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:
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.
Nosotros seguiremos utilizando la ventana de comandos de AutoCAD para ver cmo 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.
)
Hay tres reglas bsicas que sigue AutoLISP:
1.
2.
3.
El intrprete 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 imagina 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 equivale al nmero 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 cmo 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, cumpliendo as con 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 carcter /), 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 al trabajar con textos, como ya
veremos). A lo largo de los artculos del curso se mostrarn los nombres de las funciones de usuario
en maysculas simplemente para que se diferencien perfectamente del resto del cdigo.
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 puntos 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 pienses que no es tan importante aadir explicaciones en el cdigo. Pero si tienes que leer
una rutina que creaste hace meses sern muy tiles, porque seguramente no recordaras muy bien
cmo funciona. Adems, si alguien va a leer alguna de vuestras rutinas le facilitaras mucho la labor.
Al igual que a ti te ser ms sencillo leer y entender una rutina con abundantes comentarios.
Para los comentarios incluidos en el cdigo, se recomienda utilizar el siguiente mtodo:
; 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
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:
;;;____________________Eduardo Magdalena____________________;;;
;;;_________________________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 nuestro gusto (aadiendo o quitando espacios y tabulaciones), lo recomendable es
mantener el formato por defecto para el cdigo. Veamos cmo queda la funcin RAG en el editor:
los textos en color rosa (en este ejemplo no aparece ningn texto, pero veremos algunos
ejemplos ms adelante) Soy un texto
el resto de elementos, como los nombres de las funciones y de las variables utilizadas, se
muestran en color negro
2.
3.
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.
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 habra 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))
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 dentro de una funcin 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 otros mtodos para cargar nuestras rutinas.
Veamos cmo sera la funcin GAR propuesta en el tema anterior:
;;;_____________________Eduardo Magdalena_____________________;;;
;;;_________________________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 ) ; Recibe un ngulo en grados decimales
(/ (* ang pi) 180.0)
;; Multiplica el ngulo en grados decimales por Pi y lo divide por 180.0.
)
Es muy parecida a la funcin RAG, no?
Opciones 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 dnde buscar primero. De modo que si subimos nuestro
directorio hasta el primer lugar (como en la imagen), este ser el primer directorio en el que busque
AutoCAD.
(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) tambin 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. Ojo!
esta funcin no redondea, sino que elimina lo que est detrs del punto decimal.
(fix 15.8) devuelve 15.
(fix -15.8) devuelve -15
(fix 0.99) devuelve 0
(fix -0.99) devuelve 0
(SIN angulo)
Devuelve el seno de un ngulo indicado en radianes.
(sin 0) devuelve 0.0
(sin (/ pi 2)) devuelve 1.0
(COS angulo)
Funciona igual que la anterior, pero devuelve el coseno del ngulo, que recibe en radianes.
(cos 0) devuelve 1.0
(cos pi) devuelve -1.0
(SQRT numero)
Esta funcin devuelve la raz cuadrada del numero que recibe como argumento. Siempre devuelve
un nmero real, no entero.
(EXP num)
Devuelve el nmero e ( e = 2.71828 ) elevado al nmero nm. 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
(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)
Basndonos en las dos funciones anteriores y recordando que podemos ejecutar listas de
instrucciones dentro de otras (a esto le llamamos anidamiento): Cmo se obtendra la coordenada
Y del punto pto? Veamos:
De modo que (cdr pto) devuelve (Y Z). As que para obtener la coordenada Y podemos escribir:
(car (cdr pto)) devuelve la coordenada Y del punto pto.
y, cmo obtenemos la coordenada Z? Pues, volviendo a aplicar la funcin CDR para llegar a la
coordenada Z
Podras pensar que escribiendo (cdr (cdr pto)) obtenemos la coordenada Z, sin embargo no es as.
La expresin (cdr (cdr pto))devuelve una lista con un nico elemento, la coordenada Z del punto
pto. Para obtener la coordenada Z del punto pto tenemos que escribir:
(car (cdr (cdr pto))) devuelve la coordenada Z del punto pto.
En resumen, las coordenadas del punto pto se obtendran mediante:
Este ejemplo es simplemente para introducir una simple regla de mnemotecnia ya que las
funciones CAR y CDR se pueden agrupar. Para ello, existen una serie de funciones que se
denominan juntando las Aes y las Des de cAr y cDr respectivamente. El ejemplo anterior, queda:
Esto nos servir como regla para recordar el nombre de estas funciones. Debes recordar tambin
que tan solo se permiten 4 niveles de amidacin, as que entre la c y la r solo puede haber 4 letras
(Aes o Des).
Supongamos que tenemos la siguiente lista asignada a la variable lst = ((a b) (c d) (e
f)). OJO!! es una lista en la que sus elementos son a su vez listas.
Puedes definir la variable lst escribiendo: (setq lst (list (list a b) (list c d) (list e f)))
Cmo obtendramos a ??
y el elemento c?
(cDr lst) devuelve la lista sin el primer elemento, es decir ((c d) (e f)).
Ahora si hacemos (cAr (cDr lst)) devuelve el primer elemento de la lista anterior, es
decir (c d).
As que (cAr (cAr (cDr lst))) devuelve c, o lo que es lo mismo:
Cmo obtener d ?
(cDr lst) devuelve ((c d) (e f))
d)
(cAr (cDr lst)) devuelve el primer elemento de ((c d) (e f)), es decir devuelve (c
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:
Y cmo obtener e ?
(cAr (cDr (cDr lst))) devuelve (e f) tal como se vio en el ejemplo anterior.
As que (cDr (cAr (cDr (cDr lst)))) devuelve (f), que os recuerdo que se trata de una lista
y que no es lo mismo que f.
Por tanto (cAr (cDr (cAr (cDr (cDr lst))))) devuelve f.
Podramos 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 aqu estamos
intentando agrupar 5, y lgicamente no podemos. Para obtener f podramos escribir, por ejemplo:
(LENGTH lista)
En la variable pto tenamos una lista con las coordenadas de un punto, pero si solo trabajamos en
2D, la coordenada Z no nos interesa. As que muchas veces los puntos tan slo tendrn 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 producindose 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 punto pto estuviera 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 de dos elementos cada una.
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 a b) devuelve (a b)
(list d e) devuelve (d e)
(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 pasarlo a la funcin command:
(command linea (list 0.0 0.0) (list 100.0 200.0) )
Lo realmente potente de COMMAND es que podemos ejecutar casi todos los comandos de
AutoCAD. Cules 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 dependern 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.
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 anteponiendo 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.
2.
Crear y cargar una macro en la que est definido un nuevo comando con el nombre del
comando que acabamos de anular.
Veamos un ejemplo:
1.
2.
2.
1.
2.
3.
1.
(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.
cerrar AutoCAD y abrirlo de nuevo, de modo que la macro que hemos creado se borre de la
memoria del ordenador
2.
ejecutar el comando de AutoCAD redefine (En Ingls es igual, pero con un guin bajo
delante) e indicar el nombre del comando del que queremos recuperar su definicin original, es
decir linea.
Bueno, por ltimo un ejercicio: Crear una macro que defina un nuevo comando de AutoCAD
llamado CIRCPERI que dibuje una circunferencia indicando su centro y la longitud de su permetro.
Operaciones de comparacin
En este artculo veremos las funciones de AutoLISP que nos permiten realizar comparaciones, por
ejemplo, para ver si algo es mayor que algo, o menor, o si es igual.
(= expr1 expr2 )
Compara si expr1 devuelve el mismo resultado que expr2, en caso afirmativo devuelve T y en caso
contrario devuelve nil.
(= 5 (+ 1 4)) devuelve T porque (+ 1 4) devuelve 5
(= 5 (+ 1 4.0)) devuelve T aunque (+ 1 4.0) devuelve 5.0 y no 5. Pero 5 y 5.0 valen lo mismo, no?
(= 5 5.0) devuelve T
Macro de ejemplo
Veamos cmo se hara el ejercicio propuesto en el artculo 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. Bien, cmo podra ser el pseudocdigo de esta rutina?, vamos a
ver:
1.
2.
3.
4.
Dibujar la circunferencia.
Iniciamos en editor de Visual LISP Herr. > AutoLISP > Editor de Visual LISP y creamos una
nueva macro. Primero hay que aadir la definicin del nuevo comando CIRCPERI:
(defun C:CIRCPERI ( )
Vamos a ver que es lo que nos indica el pseudocdigo:
1) Pedir al usuario el centro de la circunferencia. Podramos escribir (getpoint Centro de la
circunferencia) pero tendramos que almacenar el punto indicado en una variable, as que
aadiremos
(setq pto (getpoint Centro de la circunferencia)) almacena el punto centro de la circunferencia
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 podramos cambiarlo por:
(setq peri (getreal Permetro:)) almacena el permetro de la circunferencia en la variable peri
3) Calcular el radio de la circunferencia a partir de su permetro. Peri = 2* pi * rad as que rad = Peri /
( 2 * pi). Traduciendo esta frmula a cdigo:
(setq rad (/ peri (* pi 2))) calcula el valor del radio de la circunferencia
4) Dibujar la circunferencia
(command _.circle pto rad) dibuja la circunferencia
Slo nos falta una cosa. Recuerda: 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 deberas 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.
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 continan 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)
Preguntando 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) devolver nil porque d es nil
(and a b c d) devolver nil, porque d es nil
(NOT expr)
A esta funcin le gusta llevarle la contraria a la expresin que recibe como argumento.
si la expresin devuelve cualquier cosa que no sea nil, entonces NOT devuelve nil.
2.
3.
2.
(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 completa. 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
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.
Veamos ahora algunos 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) devolvernil evalundose 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.
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 condicin (< 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 se pone la condicin T como primera condicin? Pues sucede que las que estn a
continuacin nunca se evaluarn, ya que T siempre se cumplir. Si en el ejemplo
anterior hubiramos 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 mostrar los mensajes de
alerta indicando que A no es igual a B, A no es menor que C y A no es menor que B.
(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 mostrar alguna informacin
importante.
(alert Error: Dato no vlido)
(TERPRI)
\\ Equivale al caracter \
\ Equivale al caracter
\e Equivale a ESCape
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:
;;;____________________Eduardo Magdalena____________________;;;
;;;______________________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)
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 enteros 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.
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.
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:
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)
(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 0. Siempre que escribes algo
que no sea un nmero devuelve cero.
(atoi 15.3) devuelve 15
(atoi 15.99999) devuelve 15
(FLOAT numero)
Convierte un nmero en real, as que lo lgico es que reciba un entero como argumento.
(float 5) devuelve 5.0
pero tambin podemos pasar un nmero real como argumento (float 5.36) que devuelve 5.36 lo
cual sera una tontera porque en ese caso la funcin float no hace nada.
(ATOF texto)
Convierte un texto en real.
Formato cientfico.
Pies y pulgadas.
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 el nmero para que tenga 3 decimales.
(rtos 9.99 2 0) devuelve 10
El mensaje es opcional, pero casi siempre se utiliza. 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:))
Si sustituimos la funcin getreal por getdist adems de poder escribir un permetro directamente,
tambin podremos indicarlo mediante dos puntos.
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 mantener como estaban. De modo que se desactivar momentneamente el valor de la
variable en medio de la macro 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?. Y si lo dice, le
respondes: Perdona, pero mi macro, deja las cosas tal y como estaban, as que o ya estaba cerrada
antes o la cerraron despus.
Si cmdecho est inicialmente desactivada nuestra rutina la desactiva, o lo intenta, y luego la activa al
final de la macro. 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 macro
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 antes de entrar la puerta estaba cerrada, entonces la cierro. Es decir, si cmdecho estaba
activada, entonces la vuelvo a activar.
)
(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)
Para que la salida de nuestras macros sea limpia aadiremos al final del cdigo una funcin que
devuelva una cadena de texto vaca, as no escribir nada.
Podramos pensar en utilizar la expresin (prompt ) al terminar nuestras macros. Sin embargo la
expresin anterior no devuelve una cadena de texto vaca sino que devuelve nil.
Suelen emplearse las funciones (princ) o (prin1) que an no hemos visto, pero que devuelven una
cadena de texto vaca.
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)
)
(prin1)
)
(prompt \nNuevo comando CIRCPERI cargado)
Recuerda, antes de ponerse a escribir cdigo hay que: escribir el pseudocdigo. Veamos,
podamos hacer algo as:
1.
2.
3.
4.
5.
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 podremos aadir las variables locales, si es que existen.
1) Obtener el centro de la circunferencia. Podra ser algo as:
(setq pto (getpoint \nCentro de la arandela: ))
2) Obtener el radio de la circunferencia 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)
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.
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>:
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 con la
opcin 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.
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.
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.
Aadiremos al inicio de nuestra rutina deshacer inicio y al final de la rutina deshacer fin.
Veamos:
(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 (getdist pto \nRadio interior: ))
(command _.circle pto rad)
(setq rad (getdist pto \nRadio exterior: ))
(command _.circle pto rad)
(if (= cmd0 1)
(setvar cmdecho 1)
)
(command _undo _end)
(princ)
)
(prompt \nNuevo comando ARAND cargado)
Deberamos aadir estas dos lneas en la macro CIRCPERI? Pues no es necesario, puesto que
en CIRCPERI tan solo utilizamos un comando. As que el comando de AutoCAD H va a deshacer
ese comando. En cambio en ARAND se dibujan dos circunferencias con dos comandos de
AutoCAD.
Controlar lo que hace la rutina en caso de que se produzca un error durante su ejecucin.
Impedir que el usuario introduzca datos errneos. Por ejemplo, que indique cero como radio
de una circunferencia.
En este artculo se tratar el primero de los dos puntos anteriores, y en el siguiente se ver el
segundo punto.
Si nos fijamos en el cdigo de la macro ARAND: 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. De modo que
Crear una funcin con el mismo nombre. Por ejemplo, (defun SIN Esto redefinir la
funcin SIN de AutoLISP.
2.
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.
(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 escribindolo 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.
(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 escribindolo se calcula el radio
correspondiente. En caso de que se pulse Intro, se asocia a la variable rad el radio del ltimo crculo
dibujado.
modo
Es un nmero entero que nos permitir limitar los datos que se puedan introducir en la siguiente
solicitud de datos al usuario. InitgetNUNCA 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
8 > Permite indicar un punto fuera de los lmites del dibujo. An cuando estos estn
activados.
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, entonces deberamos 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 deber 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:
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, getdistdevolver 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.
Si revisamos el cdigo de la macro CIRCPERI encontraremos la siguiente lnea:
(setq pto (getpoint \nCentro de la circunferencia: ))
deberamos aadir alguna limitacin como respuesta del usuario? Veamos:
Si el usuario pulsa Intro como respuesta a la variable pto se le asocia el valor nil, que es lo
que 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 128 permite introducir como respuesta una expresin de AutoLISP. Este cdigo
tambin se podra indicar, pero lo habitual es no hacerlo. Se podra utilizar 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. Aqu nos pide un punto, as que no tiene demasiado sentido.
Si aadimos antes del getdist la expresin (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 crculo no puede ser ni cero ni un nmero negativo, de modo que podemos
aadir a initget los cdigos 2 y 4.
Tambin podramos aadir el cdigo 64 para que GETdist devuelva la distancia en 2D. Pero
normalmente no conviene aadir este cdigo, excepto cuando la distancia deba ser siempre
ser en 2D.
Una ltima nota sobre CIRCPERI: Cuando ejecutamos la macro por primera vez en un dibujo en el
que no se ha dibujado 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. La primera solicitud que tenemos en ARAND es la del
centro de la arandela. Prcticamente es igual que la solicitud del centro del crculo en CIRCPERI,
as que le aadimos tambin el cdigo 1 a Initget:
(initget 1)
como respuesta a getdist podemos indicar una distancia, ya sea escribiendo un valor numrico o
mediante dos puntos, pero ahora tambin aceptar como respuesta Dimetro y Permetro. 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.
En [palabras_clave] indicamos una serie de palabras, separadas por espacios, que servirn 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 Dimetro desHacer)
(setq rad (getdist \nRadio del circulo / Dimetro / desHacer: ))
Para seleccionar la opcin Dimetro habr que escribir al menos la D. Pero para seleccionar
desHacer al menos habr que escribirH. Tambin aceptara di para el dimetro y desh para
deshacer.
Sin embargo no aceptar de ni des para seleccionar desHacer. Fjate que al menos deben
indicarse las letras en maysculas.
Por otra parte, aceptar tanto di como dia sin tilde para seleccionar Dimetro.
Supongamos que tenemos el siguiente cdigo:
(initget 7 Dimetro desHacer)
(setq rad (getdist \nRadio del circulo: ))
En este caso getdist aceptar como respuestas a la peticin del radio de la circunferencia
Dimetro y desHacer. Pero al no indicar estas opciones en el mensaje de getdist, el usuario no
sabr que existen. De modo que es recomendable indicar al usuario las opciones que puede
seleccionar:
(initget 7 Dimetro desHacer)
(setq rad (getdist \nRadio del circulo / Dimetro / desHacer :))
Como mejor se ve es con un ejemplo as que, vamos a modificar la rutina ARAND aadiendo una
opcin para indicar el dimetro en lugar del radio. Tenemos que modificar las siguientes lneas:
(initget 7)
(setq rad (getreal \nRadio interior: ))
Primero aadimos Dimetro a la lista de palabras clave de initget:
(initget 7 Dimetro)
Y a continuacin le decimos al usuario que existe una opcin llamada Dimetro que puede
seleccionar como respuesta:
(setq rad (getreal \nRadio interior / Diametro: ))
Si el usuario indica una distancia la asigna a la variable rad y luego (if (= rad Dimetro)
devuelve nil, puesto que rad es distinto de Dimetro.
Pero rad viene de radio, porque en esta variable almacenamos el radio de la circunferencia
y no el dimetro. As que al dibujar la circunferencia (command _.circle pto rad) dibujara
una circunferencia del doble del dimetro de lo que ha dicho el usuario.
Pues que asignara a la variable rad el resultado de dividir -50 o 0 entre 2.0. Por tanto tendramos
una circunferencia 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 Dimetro)
(setq rad (getreal \nRadio interior / Dimetro: ))
(if (= rad Dimetro)
(progn
(initget 7)
(setq rad (/ (getreal \nDimetro interior: ) 2.0))
)
)
Se ha aadido la funcin Progn ya que sino, solo se puede ejecutar una expresin como condicin
si-cumple del IF.
Para el radio o dimetro exterior se hara exactamente lo mismo. Por tanto el cdigo completo ser:
(defun C:ARAND ( / pto rad cmd0 )
(command _.undo _begin)
(if (= (setq cmd0 (getvar cmdecho)) 1)
(setvar cmdecho 0)
)
(initget 1)
(setq pto (getpoint \nCentro de la arandela: ))
(initget 7 Dimetro)
(setq rad (getdist pto\nRadio interior / Dimetro: ))
(if (= rad Dimetro)
(progn
(initget 7)
(setq rad (/ (getreal \nDimetro interior: ) 2.0))
)
)
(command _.circle pto rad)
(initget 7 Dimetro)
(setq rad (getdist pto \nRadio exterior / Dimetro: ))
(if (= rad Dimetro)
(progn
(initget 7)
(setq rad (/ (getreal \nDimetro exterior: ) 2.0))
)
)
(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 slo podr indicar como respuesta una de las palabras clave de la
funcin Initget y se la asigna a la variableopc.
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 macros 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 la opcin que indique el usuario se hace una cosa u otra.
Hasta hace poco tan slo podamos crear programas cuya ejecucin fuera lineal:
Haz esto
Luego vimos las estructuras condicionales IF y COND que ya nos permiten jugar un poco ms y
hacer que nuestros programas no fueran tan lineales. Ahora vamos a ver funciones que nos
permitir crear repeticiones de cdigo y algo que tal vez te suene, los bucles, que se utilizan mucho
en programacin.
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 Intro, getpoint devolver nil y lo almacenar en la variable pt, de modo
que (not (setq pt (getpoint \nPunto inicial: ))) devolver T, y preguntar de nuevo por un
punto.
Luego la funcin While evala la condicin flag1 que devolver su valor nil o T.
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.
(progn
(initget 7)
(setq radi (/ (getreal \nDimetro interior: ) 2.0))
)
)
(command _.circle pto radi)
(while (or (not rade) (not (< radi rade)))
(initget 7 Dimetro)
(setq rade (getdist pto \nRadio exterior / Dimetro: ))
(if (= rade Dimetro)
(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)
Haz esto
Luego vimos las estructuras condicionales IF y COND que ya nos permiten jugar un poco ms y
hacer que nuestros programas no fueran tan lineales. Ahora vamos a ver funciones que nos
permitir crear repeticiones de cdigo y algo que tal vez te suene, los bucles, que se utilizan mucho
en programacin.
Si el usuario indica Intro, getpoint devolver nil y lo almacenar en la variable pt, de modo
que (not (setq pt (getpoint \nPunto inicial: ))) devolver T, y preguntar de nuevo por un
punto.
Luego la funcin While evala la condicin flag1 que devolver su valor nil o T.
La funcin repeat ejecuta las expresiones indicadas el nmero de veces que se indique
en cantidad y devuelve el resultado de la ltima expresin evaluada.
(repeat 10 (prompt \nEste 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 hace falta fuego en un simulacro de incendio))
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) (prompt Este curso me parece muy sencillo))
o suponiendo que las variables a y b tengan asignados dos nmeros enteros:
(prompt Inspector Gadget, este mensaje se autodestruir en)
(setq i 0)
(repeat (+ a b)
(prompt \t)
(prompt (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 seguros de que la cantidad indicada es un
nmero entero y no un real. Incluso si como cantidad indicamos un nmero entero sin decimales,
como 2.0, dar un error.
Vamos a modificar la rutina ARAND para hacer que el segundo radio siempre sea mayor que el
primero. El cdigo correspondiente al crculo exterior era el siguiente:
(initget 7 Dimetro)
(setq rad (getdist pto \nRadio exterior / Dimetro: ))
(if (= rad Dimetro)
(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 exterior, 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.
Podramos sustituir el cdigo anterior por el siguiente:
(while (or (not rade) (not (< radi rade)))
(initget 7 Dimetro)
(setq rade (getreal \nRadio exterior / Dimetro: ))
(if (= rade Dimetro)
(progn
(initget 7)
(setq rade (/ (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 (notrade) devolver T.
Como (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 la circunferencia 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 Dimetro)
(setq radi (getdist pto \nRadio interior / Dimetro: ))
(if (= radi Dimetro)
(progn
(initget 7)
(setq radi (/ (getreal \nDimetro interior: ) 2.0))
)
)
(command _.circle pto radi)
(while (or (not rade) (not (< radi rade)))
(initget 7 Dimetro)
(setq rade (getdist pto \nRadio exterior / Dimetro: ))
(CHR entero)
Esta funcin devuelve el carcter al que le corresponde el cdigo ASCII indicado. Por ejemplo:
(chr 65) devuelve A
(chr 97) devuelve a
(ASCII texto)
Devuelve el cdigo ASCII (un nmero entero) correspondiente al primer carcter de la cadena de
texto indicada.
(ascii Abcde) devuelve 65
(ascii A) tambin devuelve 65 porque lo nico que importa es el primer carcter de la cadena de
texto.
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) >: )))
Pedir un nmero real al usuario y mostrar el siguiente mensaje en la ventana de AutoCAD: Radio
<10>:
# Equivale a un dgito.
Por ejemplo (wcmatch AutoLISP ~B*) comprueba que el texto no empieza por
Incluso podemos comprobar con dos patrones distintos, separados por una
coma: (wcmatch AutoLISP A*,*LISP) devolvera T.
Pero cmo hacemos 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
apstrofo 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 podemos ahora modificar la macro CIRCPERI. Tenamos las siguientes
lneas en la macro:
(prompt \nPermetro <)
(prompt (rtos (* (getvar circlerad)2 pi) 2 2))
(prompt >: )
Podramos 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 CIRCULOM que nos permitir dibujar mltiples
circunferencias concntricas. Y comenzaremos como siempre por el pseudocdigo. Podra ser algo
as:
1.
2.
Dibujar un crculo.
Aadimos (initget 1) antes de la solicitud Getpoint, para que el usuario no pueda indicar
Intro como respuesta.
Y aadimos (initget 6) antes de getdistpara que no permita ni cero ni un nmero negativo.
Observa que (initget 6) se vuelve a poner otra vez dentro del bucle While. Si no
lo hiciramos 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 si que 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 del bucle While.
Otras mejoras seran:
Aadir una lnea fuera de la funcin para que indique el nombre del comando al cargar la
macro.
(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).
(nth 2 pto) devolver la coordenada Z del punto pto, si existe y si no existe devolver nil.
(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 podemos utilizar cons y reverse:
(setq lst (reverse (cons pltanos (reverse lst))))
Veamos como funciona la lnea de cdigo anterior:
(cons pltanos (reverse lst)) aade pltanos como primer elemento de la lista
invertida
(ACAD_STRLSORT lista)
Esta funcin devuelve una lista con sus elementos, que debern ser cadenas de texto, ordenados
alfabticamente. Por ejemplo:
(acad_strlsort lst) devolver (limones manzanas naranjas peras pltanos)
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 podra hacerse sin emplear contadores, como hicimos antes. Y tambin podemos hacerlo
con FOREACH, veamos:
(foreach p lst (setq total (+ total p)))
Todo lo que antes ponamos en un bucle se pone ahora en una sola lnea de cdigo.
(mapcar * vector vector) devolver una lista con las coordenadas del vector al cuadrado,
ya que las estamos multiplicando por si mismas.
2.
3.
2.
(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 podramos 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. Si
necesitamos dividirla por 2.0. podemos crear una funcin que reciba un nmero y lo divida por 2.0
devolviendo el resultado:
(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.
(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 te ests preguntando 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.
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 sabrs 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 difcil 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 alfabticamente
Estn 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 hacamos en APPLY y MAPCAR:
(apply + (list 2.0 3.5 6.8))
pues el apstrofo 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 evaluar la expresin
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. En este caso nos sirve para contener la expresin de AutoLISP anterior.
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.Puedes hacer la prueba escribiendo !a o !b en la
ventana de comandos de AutoCAD.
(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, ya que 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 evala 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:
(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) 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?
(READ texto)
Esta funcin lee el texto que recibe y devuelve la primera palabra pero como un literal, un smbolo,
no como una cadena de texto.
(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? Por ejemplo para solicitar 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.
Qu pasa si metemos tambin nuestra funcin C:CIRCPERI? Pues no pasara nada pero Vas a
dibujar circunferencias dadas por 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 tendra un tamao descomunal.
Podemos guardar 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 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 difcil de manipular. Adems, el mayor inconveniente es
que todas nuestras rutinas se cargaran en memoria automticamente para cada archivo de dibujo,
con lo que estaremos sobrecargando la memoria del ordenador de forma innecesaria.
1.
2.
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 (alertArchivo Rag.lsp no
encontrado))
En el segundo 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 mltiples rutinas, tan solo debemos asegurarnos de que dicha funcin est
definida, es decir que se ha 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, podis 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.
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 podamos redefinirla creando nuestra 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.
(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, suponiendo 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 varios ejemplos en los que se suele
utilizar:
1.
2.
3.
4.
Por tanto, deberamos 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
)
2.
Abrir el archivo.
3.
4.
Cerrar el archivo.
(open c:\\autoexec.bat r)
Devuelve #<file c:\\autoexec.bat> es decir, el descriptor del archivo o nil si el archivo no existe.
Aunque en la lnea anterior se abre el archivo autoexec.bat en modo lectura, luego
no podramos 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. Incluso tampoco podramos cerrarlo y quedar abierto en la
memoria hasta que se cierre la sesin de AutoACD. Cmo se debe utilizar 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. Fjate en lo importante que es guardar el
descriptor del archivo en una variable, ya que si no lo hacemos no slo no podremos leer o escribir
en el archivo, tampoco podremos cerrarlo.
(close darch)
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 adiccin) de modo que debemos saber de
antemano lo que vamos a hacer con el archivo, y SLO 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 inters 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.
Si ejecutas las dos lneas de cdigo anteriores desde la ventana de comandos de AutoCAD, veras
que (prin1 a) parece devolver 5.55.5en realidad devuelve 5.5 pero el eco de mensajes, repite el
valor devuelto por la expresin (prin1 a) es decir 5.5 Por eso, aparece5.55.5
(prin1 b darch)
Escribir en Curso de AutoLISP con las comillas incluidas en el archivo C:\Nuevo.txt.
Fjate que PRIN1 puede escribir tanto cadenas de texto como nmeros. Sin embargo la
funcin PROMPT tan slo puede recibir como argumento 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.
Al igual que las funciones anteriores, si se indica un descriptor de archivo escribir en dicho
archivo en lugar de hacerlo en la ventana de comandos de AutoCAD.
(princ \nBienvenido al curso darch)
(READ-CHAR [descriptor_archivo])
Lee un carcter del archivo cuyo descriptor se indica y devuelve su cdigo ASCII.
(read-char darch) devuelve 34, que es el cdigo ASCII del primer caracter del archivo de texto, que
es unas comillas
Si no se indica el descriptor, lo lee de la ventana de comandos de AutoCAD.
(read-char) espera a que introduzcas un caracter y devolver su cdigo ASCII
(READ-LINE [descriptor_archivo])
Esta funcin lee una lnea del archivo de texto indicado.
(read-line darch) devolver Curso de AutoLISP\
Observa que las comillas iniciales del archivo ya las habamos ledo con la funcin anterior readchar.
Al leer un archivo de texto vamos avanzando en l, de modo que si volvemos a escribir la linea
anterior
(read-line darch) devolver Bienvenido al curso Muchas gracias
Esta es la ltima lnea de nuestro archivo as que si volvemos a repetir la lnea anterior
(read-line darch) devolver nil
De este modo sabremos que hemos llegado al final del archivo y podremos cerrarlo
(close darch)
Si intentamos volver a leer ahora en el archivo obtendremos un error.
Si no se indica el descriptor de archivo, la leer de la ventana de comandos de AutoCAD.
(read-line) escribe una palabra o frase y devolver una cadena de texto con lo que has escrito.