[3/13]

ONCE.8. ACCESO A VARIABLES DE AutoCAD 14

Vamos a explicar ahora, en esta sección, el control que podemos tener desde AutoLISP con respecto a las variables de sistema de AutoCAD 14.

Para lo que se refiere a este control tenemos a nuestra disposición dos funciones muy importantes y utilizadas en la programación en AutoLISP. Esta funciones son GETVAR y SETVAR. Si revisamos el MÓDULO NUEVE de este curso, acerca de la programación en lenguaje DIESEL, recordaremos la misión que realizaba la función GETVAR de este lenguaje. Pues exactamente la misma (y con el mismo nombre) realiza bajo AutoLISP. Lo único que varía es la sintaxis de la función, debido a las exigencias propias de AutoLISP, pero tampoco demasiado, es la siguiente:

(GETVAR nombre_variable)

Con GETVAR extraemos o capturamos el valor actual de la variable de sistema o acotación de AutoCAD 14 indicada en nombre_variable, o sea, de cualquier variable del programa.

Como sabemos, AutoCAD funciona internamente con multitud de variables (APÉNDICE B) que controlan prácticamente todos los aspectos del programa. El que posea el conocimiento y habilidad de manejo de las variables de AutoCAD, se puede decir que posee el control casi al 100% sobre él. Pues desde AutoLISP accederemos al contenido de dichas variables para luego procesarlo, o simplemente como información.

El nombre de la variable habrá de ir entre comillas, por ser cadena. Vemos un ejemplo:

(GETVAR "pickfirst")

Esta expresión devolverá el valor de la variable de sistema de AutoCAD 14 PICKFIRST, que controla la llamada designación Nombre-Verbo.

Otros ejemplos:

(GETVAR "blipmode")
(GETVAR "aperture")
(GETVAR "blipmode")
(GETVAR "dimtad")
(GETVAR "modemacro")

NOTA: Si la variable indicada no existe, AutoLISP devuelve nil.

Por su lado, SETVAR realiza la acción contraria, es decir, introduce o asigna un valor a una variable de AutoCAD. Su sintaxis es:

(SETVAR nombre_variable valor)

SETVAR asignará valor a nombre_variable, según esta sintaxis, y devolverá valor como respuesta. El nombre de la variable en cuestión deberá ir entre comillas, al igual que con GETVAR, y el valor que se le asigne deberá ser coherente con la información que puede guardar la variable. Si no es así, AutoLISP devuelve el error AutoCAD rejected function.

NOTA: En el APÉNDICE B, además de la lista de las variables de sistema y acotación de AutoCAD 14, se muestra también el significado de cada una de ellas y el tipo de valor que pueden guardar, así como el rango de éste o las opciones disponibles.

Veamos algún ejemplo:

(SETVAR "filletrad" 2)
(SETVAR "proxygraphics" 0)
(SETVAR "attdia" 1)

Si no existe la variable se devuelve el mismo error que si se le introduce un valor erróneo.

El funcionamiento de SETVAR cuando un comando se encuentra en curso es completamente transparente, es decir, sería como utilizar el comando MODIVAR (SETVAR en inglés, igual que la función) de AutoCAD de manera transparente, con el apóstrofo delante. En estos casos puede suceder que la modificación de la variable sólo surta efecto en la siguiente orden o en la siguiente regeneración.

Un ejemplo de total trasparencia podría ser:

(COMMAND "_erase") (SETVAR "pickbox" 2)

COMMAND llama al comando BORRA (ERASE) de AutoCAD, el cual se queda esperando en Designar objetos:. Después SETVAR cambia el valor de la mira de designación a un valor de 2. Este cambio se efectúa de manera trasparente, y la orden BORRA sigue pidiendo designar objetos, pero ahora visualiza la mirilla con el nuevo tamaño de mira de designación.

Evidentemente no se puede cambiar el valor de una variable que sea de sólo lectura. Si se intenta, se producirá el mismo error antes comentado en dos ocasiones.

NOTA: Para algunas variables como ANGBASE y SNAPANG, el valor de las mismas se interpreta en radianes al acceder mediante AutoLISP, mientras que si se accede con MODIVAR, desde la línea de comandos (o tecleando el nombre de la variable), su valor se considera en grados. Cuidado con esto. La misma consideración para GETVAR.

Un ejemplo práctico y muy usado es la posibilidad de desactivar el eco de la línea de comandos en la ejecución de programas AutoLISP. Este eco (variable CMDECHO) evitará que las funciones de AutoLISP vayan devolviendo números, cadenas y demás a lo largo de la ejecución. Y antaño, cuando las marcas auxiliares (variable BLIPMODE) venían activadas por defecto en AutoCAD, se utilizaba mucho la posibilidad de desactivarlas para producir unas rutinas "limpias". Veamos en uno de los ejemplos vistos hace poco:

(DEFUN CircEjes (/ Centro Radio)
--(INITGET 1)
--(SETQ Centro (GETPOINT "Centro del círculo: "))
--(INITGET (+ 1 2 4))
--(SETQ Radio (GETDIST Centro "Radio del círculo: "))
--(COMMAND "_circle" Centro Radio)
--(INITGET 1)
--(COMMAND "_line" Centro "_qua" "\\" "")
--(COMMAND "_line" Centro "_qua" "\\" "")
--(COMMAND "_line" Centro "_qua" "\\" "")
--(COMMAND "_line" Centro "_qua" "\\" "")
)

(DEFUN C:CircEjes ()
--(SETVAR "cmdecho" 0)
--(SETVAR "blipmode" 0)
--(CircEjes)
--(SETVAR "cmdecho" 1)
--(SETVAR "blipmode" 1)
)

Podemos observar otra aplicación a la hora de estructurar la programación. El comando de AutoCAD (C:CircEjes) sólo contiene la llamada a la función que realiza toda la tarea y las definiciones de los valores de las variables pertinentes antes de la propia llamada; restaurando sus valores al final del programa (tras la ejecución de la función).

 

ONCE.9. ESTRUCTURAS BÁSICAS DE PROGRAMACIÓN

En el mundo de los lenguajes de programación existen un par de estructuras que, con todas sus variantes, son consideradas las estructuras básicas o elementales a la hora de programar. Esta estructuras son las condicionales (o alternativas) y las repetitivas. Dentro de cada una de ellas pueden existir variantes, como decimos, que realicen el trabajo de distinta forma. Por ejemplo, y si sabemos programar algo en BASIC (QuickBASIC) pensaremos en IF... THEN... ELSE, WHILE... WEND o SELECT CASE como estructuras alternativas o condicionales y en FOR... NEXT o GOSUB... RETURN como estructuras repetitivas. Hay más, en este y en todos los lenguajes, cada una operando a su manera, pero todas dentro del mismo grupo.

Pues en AutoLISP también disponemos de una serie de funciones que nos van a permitir jugar con la posibilidad de ejecutar determinados tramos de nuestro programa si se da una condición, o repetir una serie de funciones un determinado número de veces, etcétera.

Vamos a empezar pues con la primera.

(IF condición acción_se_cumple [acción_no_se_cumple])

La función IF establece una condición en forma de expresión evaluada. Si dicha condición se cumple, es decir si el resultado es distinto de nil, entonces pasa a evaluar la expresión contenida en acción_se_cumple. En este caso devuelve el resultado de esta expresión.

Si la condición no se cumple, es nil, entonces pasa a evaluar el contenido de la expresión en acción_no_se_cumple, si es que existe (es opcional). El contenido en este caso de la acción si es que se cumple sería obviado, al igual que el contenido de la acción si no se cumple cuando se cumple.

Si no se indica acción_no_se_cumple y la condición no se cumple (no evalúa acción_se_cumple), AutoLISP devuelve nil.

Veamos un ejemplo para aclararnos un poco:

(DEFUN C:Personal ()
--(SETQ Nombre (GETSTRING T "Introduce tu nombre: "))
--(IF (= Nombre "Jonathan")
----(SETVAR "blipmode" 0)
----(SETVAR "blipmode" 1)
--)
)

Este pequeño programa ha podido ser diseñado para que pregunte por un nombre, que guardará en la variable (global) Nombre. Después se pregunta: si Nombre es igual a Jonathan, entonces se establece la variable BLIPMODE a 0, si no, se establece BLIPMODE a 1. Dependiendo del nombre que tecleemos se realizará una acción u otra.

Otro ejemplo:

(DEFUN C:Compara ()
--(SETQ Punto1 (GETPOINT "Primer punto: "))
--(SETQ Punto2 (GETPOINT "Segundo punto: "))
--(IF (EQUAL Punto1 Punto2)
----(PROMPT "Son iguales.")
----(PROMPT "No son iguales.")
--)
)

Este ejemplo acepta dos puntos introducidos por el usuario. Si dichos punto son iguales (comparados con EQUAL) el resultado de la comparación es cierto (T) por lo que se escribe el mensaje Son iguales. (acción_se_cumple); si no lo son, el resultado es nil y pasa directamente a escribir No son iguales. (acción_no_se cumple).

NOTA: Hemos conjeturado el funcionamiento de PROMPT. Aún así, lo veremos inmediatamente.

Como ya se ha dicho, la acción que se realiza si no se cumple la condición no es obligatorio ponerla. Así, podemos realizar un pequeño ejercicio en el que no haga nada ni no se cumple la condición:

(DEFUN C:Prueba ()
--(SETQ X (GETDIST "Distancia primera: "))
--(SETQ Y (GETDIST "Distancia segunda: "))
--(IF (>= X Y)
----(SETQ X (1+ X))
--)
)

Este ejemplo pregunta por dos distancias, si la primera es mayor o igual que la segunda, incrementa en una unidad esa distancia primera, si no, no se realiza absolutamente nada.

La función IF debe llevar dos argumentos como mínimo, la condición o comparación y la acción si dicha condición se cumple. La acción si no se cumple es opcional, como sabemos. Por ello, si lo que queremos es indicar una opción si no se cumple y evitar que realice algo si se cumple, habremos de indicar una lista vacía en este primero argumento:

(IF (EQUAL Pto1 Pto2) () (PROMPT "No son iguales."))

Si no se hace esto, tomaría la segunda acción como primera y no produciría el resultado esperado.

Existe una pequeña restricción en torno a la función IF, y es que únicamente permite un elemento o expresión en cada uno de sus argumentos. Por ejemplo, si hubiéramos querido indicar en el ejemplo C:Prueba un incremento de uno para X y, además un incremento de 7.5 para Y, todo ello si la condición se cumple, no habríamos podido hacerlo todo seguido. Para subsanar este pequeño inconveniente existe una función que enseguida veremos.

Antes vamos a explicar esa función PROMPT que hemos dejado un poco en el aire.

(PROMPT cadena)

PROMPT escribe la cadena de texto especificada en la línea de comandos de AutoCAD y devuelve nil. Ejemplos:

(PROMPT "Hola") devuelve Holanil
(PROMPT "Hola, soy yo")
devuelve Hola, soy yonil
(PROMPT "1 + 2")
devuelve 1 + 2nil
(PROMPT "")
devuelve nil
(PROMPT " ")
devuelve nil

Se observa que el mensaje se devuelve sin comillas.

NOTA: En configuraciones de dos pantallas, PROMPT visualiza el mensaje en ambas. Es por ello preferible a otras funciones de escritura que ya veremos más adelante.

Volvamos ahora sobre el siguiente ejemplo, ya expuesto anteriormente:

(DEFUN C:Compara ()
--(SETQ Punto1 (GETPOINT "Primer punto: "))
--(SETQ Punto2 (GETPOINT "Segundo punto: "))
----(IF (EQUAL Punto1 Punto2)
----(PROMPT "Son iguales.")
----(PROMPT "No son iguales.")
--)
)

Podemos apreciar, al correr este programa, un par de cosas. La primera es que no existe salto de línea en ningún momento de la ejecución. Una salida final de este ejercicio podría aparecer así (tras indicar los dos puntos en pantalla):


Primer punto: Segundo punto: No son iguales.nil

Esto hace realmente poco vistoso el desarrollo de una aplicación.

El segundo problema es la devolución de nil al final de una función PROMPT. Al igual que en el caso anterior, desmejora la vistosidad del programa. Para solucionar estos dos problemas vamos a exponer dos funciones, TERPRI y PRIN1. La primera (TERPRI) la explicamos a continuación y, la segunda (PRIN1), indicamos donde escribirla y no vamos a decir nada más de ella, porque la comentaremos a fondo cuando estudiemos las operaciones con archivos, que es para lo realmente sirve.

(TERPRI)

Como apreciamos, TERPRI es una función sin argumentos. La misión que tiene es la de mover el cursor al comienzo de una nueva línea. Se utiliza para saltar de línea cada vez que se escribe algún mensaje en el área de comandos de AutoCAD, a no ser que la función que escriba el mensaje salte de línea por sí sola, que las hay, ya veremos.

Así por ejemplo, podemos variar el ejemplo anterior así:

(DEFUN C:Compara ()
--(SETQ Punto1 (GETPOINT "Primer punto: ")) (TERPRI)
--(SETQ Punto2 (GETPOINT "Segundo punto: ")) (TERPRI)
--(IF (EQUAL Punto1 Punto2)
----(PROMPT "Son iguales.")
----(PROMPT "No son iguales.")
--)
)

El resultado será bastante más claro, al saltar a la línea siguiente después de cada petición.

NOTA: Podríamos haber escrito cada función TERPRI en un renglón aparte del programa, pero se suelen indicar así por estructuración: para especificar después de qué mensaje salta a una nueva línea.

Existe otro método, como deberíamos saber ya (ver el principio de este MÓDULO) para saltar de línea. Es la inclusión de los caracteres \n. Pero esto se utiliza para separar cadenas en diferentes líneas. Así, el ejemplo que venimos proponiendo podemos escribirlo:

(DEFUN C:Compara ()
--(SETQ Punto1 (GETPOINT "Primer punto: \n"))
--(SETQ Punto2 (GETPOINT "Segundo punto: \n"))
--(IF (EQUAL Punto1 Punto2)
----(PROMPT "Son iguales.")
----(PROMPT "No son iguales.")
--)
)

Pero el resultado es distinto: hace la petición del punto y salta a una nueva línea antes de que lo introduzcamos.

Por otra parte, la función PRIN1 la escribiremos como norma general al final de cada programa para producir un final "limpio" del mismo:

(DEFUN C:Compara ()
--(SETQ Punto1 (GETPOINT "Primer punto: ")) (TERPRI)
--(SETQ Punto2 (GETPOINT "Segundo punto: ")) (TERPRI)
--(IF (EQUAL Punto1 Punto2)
----(PROMPT "Son iguales.")
----(PROMPT "No son iguales.")
--)
--(PRIN1)
)

De esta forma evitamos el mensaje nil al final de la ejecución.

NOTA: Como ya hemos comentado, hablaremos profundamente de PRIN1 cuando llegue el momento, ya que tiene diversas funciones y ésta es una característica especial derivada de ellas. Por ahora, tomemos como norma lo dicho y creámonoslo sin más.

Siguiendo ahora con las estructuras alternativas que habíamos apartado un poco para ver estas funciones de escritura y salto de línea, pasemos al estudio de PROGN.

(PROGN expresión1 [expresión2...])

Esta función admite como argumentos todas las expresiones indicadas y las evalúa secuencialmente, devolviendo el valor de la última evaluada.

La siguiente expresión:

(PROGN (+ 2 3) (- 1 2) (/= 23 23) (SETQ s 5.5))

equivale a indicar todas las expresiones que incluye en sus argumentos de forma separada y continuada dentro de un programa o en la línea de comandos. Es decir, los siguientes dos ejemplos son idénticos, en cuanto a resultado:

(DEFUN C:Ejem1 ()
--(SETQ X 5 Y 23.3)
--(+ X Y)
--(- X Y)
--(/ X Y)
--(* X Y)
)

y

(DEFUN C:Ejem2 ()
--(PROGN
----(SETQ X 5 Y 23.3)
----(+ X Y)
----(- X Y)
----(/ X Y)
----(* X Y)
--)
)

Entonces, ¿para qué puede servir PROGN? PROGN se utiliza en funciones cuyo formato sólo admite una expresión en determinados argumentos y nosotros deseamos indicar más. Un ejemplo muy claro es el de la función IF. Como hemos explicado, existe esa pequeña restricción de IF que únicamente permite especificar una expresión en cada uno de sus argumentos. Con PROGN tendremos la posibilidad de especificar más de una acción, tanto si se cumple la condición como si no. Veamos un pequeño ejemplo primero y después otro más elaborado que servirá de pequeño repaso de muchos aspectos vistos hasta ahora.

(DEFUN C:Condic ()
--(SETQ Valor (GETREAL "Introduce un valor: "))
--(IF (> Valor 100)
----(PROGN
------(PROMPT "Es un valor mayor de 100.") (TERPRI)
----)
----(PROGN
------(PROMPT "Es un valor menor de 100,") (TERPRI)
------(PROMPT "¿qué te parece?")
----)
--)
(PRIN1)
)

De esta manera, cada argumento de la función IF ejecuta no sólo una expresión, sino varias. En realidad únicamente ejecuta una, PROGN, que es lo que admite IF, pero ella es una que permite evaluar más una dentro de sí misma.

Veamos ahora el ejemplo siguiente. Tiene relación con un ejercicio propuesto anterior, pero con mucho más jugo.

(DEFUN Aro (/ Centro Radio Grosor Rint Rext Dint Dext Op)
--(SETQ Centro (GETPOINT "Centro del aro: ")) (TERPRI)
--(SETQ Radio (GETDIST "Radio intermedio: ")) (TERPRI)
--(SETQ Grosor (GETDIST "Grosor del aro: ")) (TERPRI)
--(INITGET "Hueco Relleno")
--(SETQ Op (GETKWORD "Aro Hueco o Relleno (<H>/R): ")) (TERPRI)
--(IF (OR (= Op "Hueco") (= Op \n))
----(PROGN
------(SETQ Rint (- Radio (/ Grosor 2)))
------(SETQ Rext (+ Radio (/ Grosor 2)))
------(COMMAND "_circle" Centro Rext)
------(COMMAND "_circle" Centro Rint)
----)
----(PROGN
------(SETQ Dint (* (- Radio (/ Grosor 2))2))
------(SETQ Dext (* (+ Radio (/ Grosor 2))2))
------(COMMAND "_donut" Dint Dext Centro "")
----)

--)
)

(DEFUN C:Aro ()
--(SETVAR "cmdecho" 0)
--(Aro)
--(SETVAR "cmdecho" 1)
--(PRIN1)
)

(PROMPT "Nuevo comando Aro definido.") (PRIN1)

Expliquemos el ejemplo. El programa dibuja aros, huecos o rellenos, solicitando el centro del mismo, su radio intermedio y su grosor.

Se crea un nueva función de usuario a la que se atribuyen una serie de variables locales —las que luego serán utilizadas—. Se pregunta por los tres datos determinantes para el dibujo de aro (centro, radio intermedio y grosor), los cuales se guardan en tres variables (Centro, Radio y Grosor). A continuación se inicializa (INITGET) el siguiente GETKWORD para que admita dos palabras claves (Hueco y Relleno) con sus respectivas abreviaturas. Nótese que no se indica ningún código para que no admita un INTRO por respuesta, ya que luego nos será útil.

Pregunta el programa si el aro que va a dibujar será hueco o relleno. Por defecto se nos ofrece la opción correspondiente a hueco (entre corchetes angulares <> para indicarlo como los comandos típicos de AutoCAD). Aquí para tomar la opción por defecto podremos pulsar directamente INTRO (lo normal en AutoCAD), por ello nos interesaba antes poder aceptar un INTRO. Además podremos elegir teclear la opción segunda o la primera.

Seguidamente hemos de controlar la entrada del usuario que se ha guardado en la variable Op. Para ello utilizamos una función IF que nos dice que, si Op es igual a Hueco (o a h, hu, hue, huec, tanto mayúsculas como minúsculas; recordemos que la salida de GETKWORD es la indicada completa en el INITGET) o (OR) igual a un INTRO (\n, opción por defecto), se realizará todo lo contenido en el primer PROGN. Si no, se pasará a evaluar lo contenido en el segundo PROGN (argumento acción_no_se_cumple de IF). De esta forma el usuario sólo tiene dos alternativas, aro hueco o aro relleno. Si escribe otra cosa no será aceptada por GETKWORD. Así, al indicar luego en el IF que si la opción no es la de aro hueco pase por alto el primer argumento, sabremos de buena tinta que lo que no es Hueco ha de ser forzosamente Relleno.

En la secuencia de funciones para un aro hueco, se calculan el radio interior y exterior del mismo y se dibujan dos círculos concéntricos que representan el aro. Por su lado, en la secuencia para un aro relleno, se calculan los diámetros interior y exterior y se dibuja una arandela. La razón para calcular diámetros aquí es que el comando ARANDELA (DONUT en inglés) de AutoCAD solicita diámetros y no radios.

Tras cerrar todos los paréntesis necesarios —el del último PROGN, el del IF y el de DEFUN— se pasa a crear el comando propio para AutoCAD (C:Aro). De desactiva el eco de mensajes en la línea de comandos, se llama a la función (Aro), se vuelve a activar el eco y se introduce una expresión PRIN1 para un final "limpio" del programa (sin nil ni ningún otro eco o devolución de AutoLISP).

Por último, y fuera de cualquier DEFUN, se introduce una función PROMPT que escribe un mensaje en la línea de comandos. Todas las funciones de AutoLISP que no estén contenidas dentro de los DEFUN en un programa se ejecutan nada más cargar éste. Por ello, al cargar este programa aparecerá únicamente el mensaje Nuevo comando Aro definido. Y al ejecutar el comando, escribiendo Aro en línea de comandos, este PROMPT no se evaluará al no estar dentro de ningún DEFUN.

El PRIN1 detrás de este último PROMPT hace que no devuelva nil. Tampoco se ejecutará al correr el programa, ya que está fuera de los DEFUN, sino sólo al cargarlo. Es por ello, que para el programa en sí se utilice otro PRIN1, el expuesto antes e incluido en el segundo DEFUN.

(COND (condición1 resultado1) [(condición2 resultado2)...])

La función COND de AutoLISP que vamos a ver ahora establece varias condiciones consecutivas asignando diferentes resultados a cada una de ellas. Es decir, es una generalización de la función IF que, sin embargo, resulta más cómoda a la hora de establecer diversas comparaciones. Veamos un ejemplo sencillo:

(DEFUN Compara ()
--(SETQ X (GETREAL "Introduce el valor de X entre 1 y 2: "))
--(COND ((= X 1) (PROMPT "Es un 1.") (TERPRI))
--------((= X 2) (PROMPT "Es un 2.") (TERPRI))
--------((< X 1) (PROMPT "Es menor que 1, no vale.") (TERPRI))
--------((> X 2) (PROMPT "Es mayor que 2, no vale.") (TERPRI))
--------(T (PROMPT "Es decimal entre 1 y 2.") (TERPRI))
--)
)

Se establecen una serie de comparaciones que equivaldría a una batería de funciones IF seguidas. En la última condición no es una lista, sino el valor de cierto T. Esto garantiza que, si no se han evaluado las expresiones anteriores se evalúen las de esta última lista. Y es que COND no evalúa todas las condiciones, sino que va inspeccionándolas hasta que encuentra una que sea diferente de nil. En ese momento, evalúa las expresiones correspondientes a esa condición y sale del COND, sin evaluar las siguientes condiciones aunque sean T.

Si se cumple una condición y no existe un resultado (no está especificado), COND devuelve el valor de esa condición.

Una aplicación muy típica de COND es el proceso de las entradas por parte del usuario en un GETKWORD. Por ejemplo:

(DEFUN Proceso ()
--(INITGET 1 "Constante Gradual Proporcional Ninguno")
--(SETQ Op (GETKWORD "Constante/Gradual/Proporcional/Ninguno: ")
--(COND ((= Op "Constante") (Constante))
--------((= Op "Gradual") (Gradual))
--------((= Op "Proporcional") (Proporcional))
--------((= Op "Ninguno") (Ninguno))
--)
)
...

En este ejemplo se toman como condiciones las comparaciones de una respuesta de usuario frente a GETKWORD, haciendo llamadas a funciones diferentes dentro del mismo programa según el resultado.

NOTA: Como podemos observar, los paréntesis indicados en la sintaxis tras COND son obligatorios (luego cerrarlos antes de la segunda condición). Estas listas engloban cada condición y resultado por separado.

NOTA: Como observamos en el primer ejemplo, con COND podemos especificar más de una expresión para el resultado de una comparación, y sin necesidad de PROGN. La primera lista se toma como condición y todas las demás, hasta que se cierre el paréntesis que engloba a una condición con sus respectivos resultados, se toman como resultados propios de dicha condición.

Y continuando con las estructuras básicas de la programación, vamos a ver ahora una muy recurrida y usada; se trata de REPEAT. REPEAT representa la estructura repetitiva en AutoLISP y sus sintaxis es la siguiente:

(REPEAT veces expresión1 [expresión2...])

Esta función repite un determinado número de veces (especificado en veces) la expresión o expresiones que se encuentren a continuación, hasta el paréntesis de cierre de REPEAT. El número de repeticiones ha de ser positivo y entero. REPEAT evaluará dicho número de veces las expresiones contenidas y devolverá el resultado de la última evaluación. Veamos un ejemplo:

(DEFUN Poligonal ()
--(SETQ Vert (GETINT "Número de vértices de la poligonal: "))
--(SETQ Lin (- Vert 1))
--(SETQ Pto1 (GETPOINT "Punto primero: "))
--(REPEAT Lin
----(SETQ Pto2 (GETPOINT "Siguiente punto: "))
----(COMMAND "_line" Pto1 Pto2 "")
----(SETQ Pto1 Pto2)
--)
)

El ejemplo pide el número de vértices de una poligonal que se dibujará con líneas. Evidentemente el número de líneas que se dibujarán será el número de vértices menos uno, por lo que se establece en la variable Lin dicho valor. Tras pedir el primer punto se comienza a dibujar las líneas en la estructura repetitiva (tantas veces como líneas hay). Lo que hace la línea (SETQ Pto1 Pto2) es actualizar la variable Pto1 con el valor de Pto2 cada vez que se dibuja una línea. De esta forma se consigue tomar como punto de la primera línea el punto final de la anterior.

(WHILE condición expresión1 [expresión2...])

La función WHILE establece estructuras repetitivas al igual que REPEAT. La diferencia estriba en que WHILE proporciona un control sobre la repetición, ya que la serie de expresiones (o única expresión como mínimo) se repetirá mientas se cumpla una determinada condición especificada en condición.

Mientras el resultado de la condición sea diferente de nil (o sea T), WHILE evaluará las expresiones indicadas. En el momento en que la condición sea igual a nil, WHILE terminará, dejando de repetirse el ciclo. Veamos el anterior ejemplo de REPEAT un poco más depurado con WHILE:

(DEFUN Poligonal ()
--(SETQ Vert (GETINT "Número de vértices de la poligonal: "))
--(SETQ Lin (- Vert 1))
--(SETQ Pto1 (GETPOINT "Punto primero: "))
--(WHILE (> Lin 0)
----(SETQ Pto2 (GETPOINT "Siguiente punto: "))
----(COMMAND "_line" Pto1 Pto2 "")
----(SETQ Pto1 Pto2)
----(SETQ Lin (1- Lin))
--)
)

De esta forma se establece una estructura repetitiva controlada por el número de líneas, el cual va decrementándose en -1: (SETQ Lin (1- Lin)) cada vez que se repite el proceso. Mientras Lin sea mayor de 0 se dibujarán líneas, en el momento en que no sea así se terminará el proceso.

WHILE se utiliza mucho para controlar entradas de usuario y procesar errores, por ejemplo:

...
(SETQ DiaCj (GETREAL "Diámetro de la cajera: ")
(SETQ Dia (GETREAL "Diámetro del agujero: "))
(WHILE (> Dia DiaCj)
--(PROMPT "El diámetro del agujero debe ser menor que el de la cajera.\n")
--(SETQ Dia (GETREAL "Diámetro del agujero: "))
)
...

Existe una forma muy particular de usar funciones como WHILE o IF. Vemos el ejemplo siguiente:

(DEFUN Haz (/ ptb pt)
--(INITGET 1)
--(SETQ ptb (GETPOINT "Punto de base: ")) (TERPRI)
--(WHILE (SETQ pt (GETPOINT ptb "Punto final (INTRO para terminar): ")) (TERPRI)
--(COMMAND "_line" ptb pt "")
)

El ejemplo dibuja segmentos rectos en forma de haz de rectas desde un punto de base a diversos puntos que es usuario introduce. Examinemos cómo se realiza la comparación en el WHILE. De suyo la comparación no existe como tal, pero sabemos que WHILE continúa mientras no obtenga nil. Ahí está el truco. En el momento en el pulsemos INTRO, pt guardará nil, por lo que WHILE no continuará. Si introducimos puntos, WHILE no encuentra nil por lo que realiza el bucle.

A continuación vamos a ver tres funciones que no se refieren a repetición de expresiones en sí, sino a repeticiones de proceso con elementos de listas. Estas tres funciones son FOREACH, APPLY y MAPCAR.

(FOREACH variable lista expresión)

Esta función procesa cada elemento de una lista (lista) aplicándole una expresión (expresión) indicada. Para ello se utiliza un símbolo (variable) que debe aparecer en dicha expresión. El funcionamiento es el siguiente: se toma cada elemento de la lista y se hace intervenir en la expresión en los lugares donde aparece el símbolo. Después se evalúa cada una de las expresiones resultantes para cada elemento de la lista. Vamos a estudiar un ejemplo:

(FOREACH Var ’(10 20 30) (* 2 Var))

Lo que se pretende aquí es multiplicar cada uno de los elementos de la lista por 2. De esta forma, y como hemos explicado, en principio se define una variable (Var). Esta variable será sustituida por cada uno de los elementos de la lista que sigue en la expresión del final. Así, Var es sustituía por 10, por 20 y por 30 respectivamente en la expresión del producto que se indica en último lugar.

Al final, FOREACH devuelve el resultado de la última expresión evaluada.

Veamos otro ejemplo que dibuja líneas desde el punto 0,0 hasta cuatro puntos 2D indicados en una lista:

(FOREACH Pto ’((10 10) (20 20) (25 40) (100 170)) (COMMAND "_line" "0,0" Pto ""))

(APPLY función lista)

APPLY aplica la función indicada a todos los elementos de una lista también indicada. Ejemplo:

(APPLY ’* ’(2 3 4))

Este ejemplo aplica la función * inherente a AutoLISP a la lista especificada. El resultado habría sido el mismo que si hubiéramos escrito:

(* 2 3 4)

aunque en determinadas situaciones puede ser interesante su uso.

Se aprecia que tanto la lista (como ya sabíamos) como la función indicada han de llevar un apóstrofo delante al ser literales. La función puede ser una subr de AutoLISP o una función definida previamente por el usuario.

(MAPCAR función lista1... listan)

Por su lado, MAPCAR aplica la función indicada a elementos sucesivos de listas. Por ejemplo, supongamos n listas cada una con un número m de elementos. MAPCAR aplicará la función especificada al primer elemento (1-1, 2-1,... n-m) de cada lista (lista1, lista2,... listan) y el resultado será guardado como primer elemento de la lista de resultado. Después realiza lo mismo con los m elementos de las n listas. El resultado final será una lista cúmulo de los resultados parciales. Veamos un ejemplo sencillo:

(MAPCAR ’+ ’(8 2 3) ’(2 1 1) ’(0 0 0))

El resultado será:

(10 3 4)

Las mismas consideraciones en cuanto a literales que para APPLY.

A continuación vamos a estudiar aquí una función que no es que tenga que ver con estas últimas, pero se suele utilizar con ellas, sobre todo con APPLY y MAPCAR. Esta función es:

(LAMBDA argumentos expresión1 [expresión2...])

LAMBDA define una función de usuario sin nombre. Su formato y funcionamiento es el mismo que DEFUN, pero al no tener nombre sólo puede utilizarse en el momento de definirla y no puede ser llamada posteriormente. Se utiliza cuando se necesita definir una función sólo momentáneamente y no se desea ocupar espacio en memoria de manera innecesaria.

LAMBDA devuelve el valor de la última expresión evaluada, lo mismo que DEFUN. Se puede usar en combinación con APPLY y MAPCAR —como decíamos— para aplicar una función temporal a los elementos de una o varias listas:

(APPLY ’(LAMBDA (x y z) (/ (- x y) z))
--’(25 5 2)
)

En el ejemplo se define una función temporal con tres variables. Su cometido es restarle y a x y dividir el resultado entre z. Se aplica esa función con APPLY a la lista que suministra los tres argumentos requeridos. El resultado será (25 - 5) / 2, es decir 10.

De manera similar se utiliza con MAPCAR, cuando se quiere obtener una lista de resultados. Por ejemplo una función para dibujar líneas entre una serie de puntos iniciales y una serie de puntos finales podría ser:

(MAPCAR '(LAMBDA (pin pf) (COMMAND "linea" pin pf ""))
--(LIST pin1 pin2 pin3)
--(LIST pf1 pf2 pf3)
)

 

7ª fase intermedia de ejercicios

· Realizar un programa que dibuje círculos concéntricos. La aplicación solicitará el centro de la serie de círculos, al número de círculos y el radio interior y exterior del conjunto. Los círculos se dispondrán de manera equidistante.

· Realizar un programa que dibuje círculos concéntricos a partir de un círculo base. Los radios de los demás círculos se irán introduciendo a medida que se dibujan (por el usuario).

 

ONCE.10. MANEJO DE LISTAS

En esta sección, y avanzando un poco más en este curso, vamos a ver una serie de funciones de AutoLISP muy sencillas que se utilizan para el manejo de listas. Ya hemos visto en varios ejemplos tipos de listas, como las de las coordenadas de un punto, por ejemplo. Aprenderemos ahora a acceder o capturar todo o parte del contenido de una lista, así como a formar listas con diversos elemento independientes. El tema es corto y fácilmente asimilable, pero no por ello menos importante, ya que esta característica se utiliza mucho en la programación de rutinas AutoLISP, sobre todo a la hora de acceder a la Base de Datos interna de AutoCAD.

Lo primero que vamos a ver es cómo acceder a elementos de una lista. Para ello disponemos de una serie de funciones que iremos estudiando desde ahora.

(CAR lista)

La función CAR de AutoLISP devuelve el primer elemento de una lista. Si se indica una lista vacía () se devuelve nil, si no se devuelve al valor del elemento. Veamos un ejemplo. Si queremos capturar la coordenada X, para su posterior proceso, de un punto introducido por el usuario, podríamos introducir las líneas siguientes en nuestro programas:

(SETQ Coord (GETPOINT "Introduce un punto: "))
(SETQ X (CAR Coord))

De esta manera, guardamos en la variable X el primer elemento de la lista guardada en Coord, es decir la coordenada X del punto introducido por el usuario.

Recordemos que si se emplean listas directamente, éstas han de ir indicadas como literales (precedidas del apóstrofo):

(CAR ’(5 20 30))

Si la lista sólo tiene un elemento se devuelve dicho elemento. Vemos unos ejemplos:

(CAR ’((/ 1 2.2) -80.2 -23.002 (* 2 3.3))) devuelve (/ 1 2.2)
(CAR ’(34.45 décimo -12))
devuelve 34.45
(CAR ’(x y z))
devuelve X
(CAR ’(3))
devuelve 3

(CDR lista)

Esta función devuelve una lista con los elementos segundo y siguientes de la lista especificada. Esto es, captura todos los elementos de una lista excepto el primero (desde el segundo, inclusive, hasta el final) y los devuelve en forma de lista. Si se especifica una lista vacía, CDR devuelve nil. Ejemplos:

(CDR ’(8 80.01 -23.4 23 34.67 12)) devuelve (80.01 -23.4 23 34.67 12)
(CDR ’(x y z))
devuelve (Y Z)
(CDR (CAR ’((1 2 4) (3 5 7) (8 1 2))))
devuelve (2 4)

Si se indica un lista con dos elementos, CDR devuelve el segundo de ellos pero, como sabemos, en forma de lista. Para capturar una segunda coordenada Y de un punto 2D por ejemplo, habríamos de recurrir a la función CAR —vista antes— para obtener dicho punto. Véanse estos dos ejemplos:

(CDR ’(30 20)) devuelve (20)
(CAR (CDR ’(30 20)))
devuelve 20

De esta manera, es decir, con la mezcla de estas dos funciones se puede obtener la coordenada Y de cualquier punto, o el segundo elemento de cualquier lista, que es lo mismo:

(CAR (CDR ’(20 12.4 -3))) devuelve 12.4
(CAR (CDR ’(34 -23.012 12.33)))
devuelve -23.012
(CAR (CDR ’(23 12)))
devuelve 12
(CAR (CDR ’(10 20 30 40 50 60)))
devuelve 20

Si se especifica una lista con sólo un elemento, al igual que con listas vacías se devuelve nil.

NOTA: Si la lista es un tipo especial de lista denominado par punteado con sólo dos elementos (se estudiará más adelante), CDR devuelve el segundo elemento sin incluirlo en lista alguna. Este tipo de listas es fundamental en la Base de Datos de AutoCAD, como se verá en su momento, y de ahí la importancia de estas funciones para acceder a objetos de dibujo y modificarlos.

Las funciones siguientes son combinaciones permitidas de las dos anteriores.

 


(CADR lista)

Esta función devuelve directamente el segundo elemento de una lista. Equivale por completo a (CAR (CDR lista)). De esta forma resulta mucho más cómoda para capturar segundos elementos, como por ejemplo coordenadas Y. Ejemplos:

(CADR ’(10 20 34)) devuelve 20
(CADR ’(23 -2 1 34 56.0 (+ 2 2)))
devuelve -2
(CADR ’(19 21))
devuelve 21
(CADR ’(21))
devuelve nil
(CADR ’())
devuelve nil

El resto de las funciones más importante se explicarán con un solo ejemplo, el siguiente:

(SETQ ListaElem ’((a b) (x y)))

(CAAR lista)

(CAAR ListaElem) devuelve A

Equivale a (CAR (CAR ListaElem)).

(CDAR lista)

(CDAR ListaElem) devuelve (B)

Equivale a (CDR (CAR ListaElem)).

(CADDR lista)

(CADDR ListaElem) devuelve nil

Equivale a (CAR (CDR (CDR ListaElem))).

(CADAR lista)

(CADAR ListaElem) devuelve B

Equivale a (CAR (CDR (CAR ListaElem))).

(CADDAR lista)

(CADDAR ListaElem) devuelve A

Equivale a (CAR (CDR (CDR (CAR ListaElem)))).

Y así todas las combinaciones posibles que podamos realizar. Como se ha visto, para obtener el tercer elemento (coordenada Z por ejemplo) de una lista utilizaremos CADDR:

(CADDR ’(30 50 75)) devuelve 75

En el ejemplo anterior, esta función habíamos visto que devolvía nil. Esto es porque era una lista de dos elementos, y si el elemento buscado no existe se devuelve, nil. Por ejemplo:

(CDDDR ’(30 60 90)) devuelve nil

La manera de construir funciones derivadas es bien sencilla. Todas comienzan con C y terminan con R. En medio llevan la otra letra, ya sea la A de CAR o la D de CDR, tantas veces como se repita la función y en el mismo orden. Veamos unos ejemplos:

CAR-CAR-CAR = CAAAR, p.e. (CAR (CAR (CAR ListaElem)))
CDR
-CDR-CDR-CAR = CDDDAR, p.e. (CDR (CDR (CDR (CAR ListaElem))))
CAR
-CDR-CAR-CAR-CDR-CDR = CADAADDR, p.e. (CAR (CDR (CAR (CAR (CDR (CDR ListaElem))))))

Y así sucesivamente. Todas esta combinaciones son extremadamente útiles, tanto para manejar listas en general como para gestionar directamente la Base de Datos de AutoCAD.

Veamos ahora otra función muy útil y versátil.

(LIST expresión1 [expresión2...])

La función LIST reúne todas las expresiones indicadas y forma una lista con ellas, la cual devuelve como resultado. Se debe indicar al menos una expresión.

Imaginemos que queremos formar una lista de las tres coordenadas de un punto obtenidas por separado y guardadas en tres variables llamadas X, Y y Z. X vale 10, Y vale 20 y Z vale 30. Si hacemos:

(SETQ Punto (X Y Z))

AutoLISP devuelve error: bad function. AutoLISP intenta evaluar el paréntesis porque es una lista. Al comenzar comprueba que X no es ninguna función y da el mensaje de error.

Si hacemos:

(SETQ Punto ’(X Y Z))

La lista con las tres coordenadas se guarda en Punto, pero ojo, como un literal. Si introducimos ahora lo siguiente el resultado será el indicado:

!Punto devuelve (X Y Z)

Para ello tenemos la función LIST por ejemplo. Hagamos ahora lo siguiente:

(SETQ Punto (LIST X Y Z))

Y ahora, al hacer lo que sigue se devuelve lo siguiente:

!Punto devuelve (10 20 30)

Hemos conseguido introducir valores independientes en una lista asignada a una variable.

Vamos a ver un ejemplo de un programa que utiliza estas funciones. El listado del código es el siguiente:

(DEFUN Bornes (/ pti dia ptf ptm)
--(INITGET 1)
--(SETQ pti (GETPOINT "Punto inicial de conexión: "))(TERPRI)
--(INITGET 5)
--(SETQ dia (GETREAL "Diámetro de bornes: "))(TERPRI)
--(WHILE (SETQ ptf (GETPOINT "Punto de borne (INTRO para terminar): "))
----(TERPRI)
----(SETQ ptm (LIST (CAR pti) (CADR ptf)))
----(COMMAND "_line" pti "_non" ptm "_non" ptf "")
----(COMMAND "_donut" "0" (+ dia 0.0000001) "_non" ptf "")
--)
)

(DEFUN c:bornes ()
--(SETVAR "cmdecho" 0)
--(Bornes)
--(SETVAR "cmdecho" 1)(PRIN1)
)

(PROMPT "Nuevo comando BORNES definido")(PRIN1)

NOTA: En programas que definan más de una función (este no es el caso), sin contar la que empieza con C:, deberemos de poner cuidado a la hora definir variables locales. Si lo hacemos por ejemplo en un DEFUN y luego otro necesita de esas variables, el segundo no funcionará. Las variables locales únicamente funcionan para su función, es decir para su DEFUN. La forma de conseguir que fueran variables locales compartidas —sólo dentro del propio programa— sería declarándolas en el DEFUN que sea comando de AutoCAD (C:).

Este último ejemplo solicita los datos necesarios y comienza el bucle de WHILE. La condición es un tanto extraña pero fácil de comprender. Sabemos que WHILE acepta una condición como válida si no devuelve nil, por lo tanto la condición es el propio valor de la variable ptf. Al darle un valor mediante GETPOINT, WHILE continuará. En el momento en que pulsemos INTRO para terminar el programa, ptf no tendrá valor, será nil, por lo que WHILE no prosigue y acaba.

El bucle lo que realiza es guardar en la variable ptm el valor de una lista, formada mediante la función LIST, y que guarda el primer elemento de la lista guardada en pti (punto inicial de conexión), es decir la coordenada X, y el segundo elemento de la lista guardada en ptf (punto de situación del borne), la coordenada Y. Después se dibujan la línea vertical y horizontal de conexión y el borne en el extremo (mediante ARANDELA).

 

8ª fase intermedia de ejercicios

· Realizar un programa que dibuje rectángulos con grosor y con esquinas redondeadas. Se solicitará al usuario el grosor del rectángulo, el radio de redondeo de las esquinas y la primera y segunda esquina del rectángulo en sí.

· Realícese un programa que dibuje ventanas con celosía en cruz. Al usuario se le solicitará el grosor de rectángulo exterior y la anchura de los marcos. Así también, evidentemente, la posición de dos vértices opuestos por una de las diagonales del rectángulo.

 

ONCE.11. FUNCIONES DE CONVERSIÓN DE DATOS

De lo que hablaremos en esta sección es de la posibilidad que tenemos mediante AutoLISP de conversión de los tipos de datos disponibles para utilizar, esto es, valores enteros, valores reales, ángulos, distancias y cadenas de texto alfanumérico. Además, y en último término, se explicará una función que es capaz de convertir cualquier valor de un tipo de unidades a otro.

Con lo que comenzaremos será con una función capaz de convertir cualquier valor (entro o real) en un valor real. Esta función es la siguiente:

(FLOAT valor)

valor determina el número que queremos convertir. Si es real lo deja como está, si el entero lo convierte en real. Veamos unos ejemplos:

(FLOAT 5) devuelve 5.0
(FLOAT 5.25)
devuelve 5.25
(FLOAT -3)
devuelve -3.0
(FLOAT 0)
devuelve 0

(ITOA valor_entero)

Esta otra función convierte un valor entero, y sólo entero, en una cadena de texto que contiene a dicho valor. Por ejemplo:

(ITOA 5) devuelve "5"
(ITOA -33)
devuelve "-33"

ITOA reconoce el signo negativo si existe y lo convierte en un guión.

Esta función resultará especialmente útil cuando se explique en este mismo MÓDULO la interacción con letreros de diálogo en DCL. Además, puede servir para introducir valores de variables en una concatenación de cadenas, por ejemplo, que próximamente veremos.

NOTA: Si se especifica un número real o una cadena como argumento de ITOA se produce un error de AutoLISP.

(RTOS valor_real [modo [precisión]])

RTOS convierte valores reales en cadenas de texto. Al contrario que ITOA, RTOS admite números enteros. Veamos algún ejemplo:

(RTOS 33.4) devuelve "33.4"
(RTOS -12)
devuelve "-12"

El argumento modo se corresponde con la variable de AutoCAD 14 LUNITS. Es decir, solamente puede ser un número entero entre 1 y 5 cuyo formato es el que se indica:

Modo -------- Formato

-----------------------------------------------------------------

1 ----------- Científico

2 ----------- Decimal

3 ----------- Pies y pulgadas I (fracción decimal)

4 ----------- Pies y pulgadas II (fracción propia)

5 ----------- Fraccionario

Si no se especifica se utiliza el formato actual de la variable en cuestión. Así:

(RTOS 34.1 1) devuelve "3.4100E+01"
(RTOS 34.1 2)
devuelve "34.1"
(RTOS 34.1 3)
devuelve "2'-10.1''"
(RTOS 34.1 4)
devuelve "2'-10 1/8"
(RTOS 34.1 5)
devuelve "34 1/8"

El argumento precisión se corresponde con la variable LUPREC e indica la precisión en decimales para la cadena de texto que se desea obtener. Si no se indica, y al igual que con el argumento modo, se supone el valor de variable en la sesión actual de dibujo. Así:

(RTOS (/ 1 3) 2 0) devuelve "0"
(RTOS (/ 1 3) 2 1)
devuelve "0.3"
(RTOS (/ 1 3) 2 4)
devuelve "0.3333"
(RTOS (/ 1 3) 2 13)
devuelve "0.3333333333333"
(RTOS (/ 1 3) 2 16)
devuelve "0.3333333333333333"

NOTA: Como deberíamos saber, AutoCAD internamente trabaja siempre con 16 decimales, indique lo que se le indique, otra cosa es la forma en que nos devuelva los resultados. Es por ello que a RTOS podemos indicarle una precisión superior a dieciséis, pero lo máximo que nos va a devolver serán esos dieciséis decimales.

Otros ejemplos:


(RTOS 2.567 1 2) devuelve "2.57E+00"
(RTOS -0.5679 5 3)
devuelve "-5/8"
(RTOS 12 3 12)
devuelve "1'"

NOTA: La variable UNITMODE tiene efecto en los modos 3, 4 y 5.

(ANGTOS valor_angular [modo [precisión]])

Esta subr de AutoLISP toma el valor de un ángulo y lo devuelve como cadena de texto. Dicho valor habrá de ser un número en radianes.

El argumento modo se corresponde con la variable AUNITS de AutoCAD 14. Sus valores están en el intervalo de 0 a 4 según la siguiente tabla:

Modo ------------ Formato

---------------------------------
0 ----------- Grados

1 ----------- Grados/minutos/segundo

2 ----------- Grados centesimales

3 ----------- Radianes

4 ----------- Unidades geodésicas

Por su lado, precisión se corresponde con la variable AUPREC de AutoCAD. Especifica el número de decimales de precisión. Veamos algunos ejemplos:


(ANGTOS PI 0 2)
devuelve "180"
(ANGTOS 1.2 3 3)
devuelve "1.2r"
(ANGTOS (/ PI 2.0) 0 4)
devuelve "90"
(ANGTOS 0.34 2 10)
devuelve "21.6450722605g"
(ANGTOS -0.34 2 10)
devuelve "378.3549277395g"

El ángulo indicado puede ser negativo, pero el valor es siempre convertido a positivo entre 0 y 2p.

NOTA: La variable UNITMODE afecta sólo al modo 4.

Veamos ahora las funciones inversas o complementarias a estas tres últimas explicadas.

(ATOI cadena)

ATOI convierte la cadena especificada en un número entero. Si la cadena contiene decimales la trunca. Ejemplos:

(ATOI "37.4") devuelve 37
(ATOI "128")
devuelve 128
(ATOI "-128")
devuelve -128

Si ATOI encuentra algún carácter ASCII no numérico en la cadena, únicamente convierte a numérico hasta dicho carácter. Si no hay ningún carácter numérico y son todos no numéricos, ATOI devuelve 0. Por ejemplo:

(ATOI "-12j4") devuelve -12
(ATOI "casita")
devuelve 0

(ATOF cadena)


Convierte cadenas en valores reales. Admite el guión que convertirá en signo negativo. Las mismas consideraciones con respecto a caracteres no numéricos que para
ATOI. Ejemplos:

(ATOF "35.78") devuelve 35.78
(ATOF "-56")
devuelve -56.0
(ATOF "35,72")
devuelve 35.0
(ATOF "23.3h23)
devuelve 23.3
(ATOF "pescado")
devuelve 0.0

(DISTOF cadena [modo])

DISTOF convierte una cadena en número real. El argumento modo especifica el formato del número real y sus valores son los mismos que los explicados para RTOS. Si se omite modo se toma el valor actual de LUNITS. Se pueden probar los ejemplos inversos a RTOS, son complementarios.

(ANGTOF cadena [modo])

Convierte una cadena de texto, que representa un ángulo en el formato especificado en modo, en un valor numérico real. modo admite los mismo valores que ANGTOS. Si se omite modo se toma el valor actual de la variable AUNITS. Se pueden probar los ejemplos inversos a ANGTOS, son complementarios.

 

ONCE.11.1. Conversión de unidades

Veamos ahora una última función un poco diferente. CVUNIT convierte un valor indicado de un tipo de unidades a otro, ambos también especificado en la sintaxis. Dicha sintaxis es la que sigue:

(CVUNIT valor unidad_origen unidad_destino)

valor representa el valor numérico que se desea convertir. unidad_origen y unidad_destino son, respectivamente, la unidades actuales del valor y la unidades a las que se quiere convertir.

Estos dos argumentos últimos hay que especificarlos como cadenas (entre comillas dobles). Los nombres que contengan dichas cadenas deberán existir en el archivo ACAD.UNT, archivo de conversión de unidades suministrado con AutoCAD 14 precisamente para el buen funcionamiento de esta función de AutoLISP. Este archivo es ASCII y puede ser editado y personalizado, por ello, vamos a estudiar aquí y ahora cómo crear nuestras propias definiciones de conversión de unidades.

 

ONCE.11.1.1. Personalizar el archivo ACAD.UNT

Al abrir este archivo mediante un editor ASCII podremos observar que se asemeja completamente a muchos de los archivos personalizables de AutoCAD 14 que ya hemos aprendido a modificar y crear en otros MÓDULOS de este curso, como por ejemplo a los de definiciones de tipos de línea, patrones de sombreado o formas. El motivo de que se haya dejado esta explicación para este punto es la relación entre este archivo ACAD.UNT y la función CVUNIT de AutoLISP.

El archivo de definición de unidades de AutoCAD, ACAD.UNT, permite definir factores para convertir datos de un sistema de unidades a otro. Estas definiciones, que son utilizadas por la función de conversión de unidades CVUNIT de AutoLISP, deben incluirse en este archivo en formato ASCII.

Cada definición consta de dos líneas en el archivo: el nombre de la unidad y su definición. La primera línea debe llevar un asterisco (*) en la primera columna, seguido del nombre de la unidad. Este nombre puede llevar varias abreviaturas o formas de escritura alternativas separadas por comas. El siguiente formato permite incluir un nombre de unidad en singular y en plural:

*[ [común] [ ( [singular.] plural) ] ]...

Pueden especificarse varias expresiones (singular y plural). No es necesario que vayan situadas al final de la palabra, y tampoco es necesario incluir la forma en plural. Ejemplos:

*pulgada(s)
*mileni(o.os)
*pi(e.es)
*metro(s),meter(s),metre(s),m

En esta última línea por ejemplo, la unidad definida permite llamarla después como argumento de CVUNIT de las formas siguientes: metro, metros, meter, meters, metre, metres o m. En el caso de la unidad de medida en pies del ejemplo: pie o pies.

La línea que sigue a esta primera define la unidad como fundamental o derivada. Una unidad fundamental es una expresión formada por constantes. Toda línea que siga a la del nombre de la unidad y no empiece por un signo igual, define una unidad fundamental. Consta de cinco enteros y dos números reales, de la siguiente forma:

c, e, h, k, m, r1, r2

 


Los cinco enteros corresponden a los exponentes de estas cinco constantes:

Constante --- Significado

--------------------------------------------------------
C
------------ Velocidad de la luz en el vacío

E ------------ Carga del electrón

H ------------ Constante de Planck

K ------------ Constante de Boltzman

M ------------ Masa del electrón en reposo

Todos estos exponentes juntos definen la magnitud medida por la unidad: longitud, masa, tiempo, volumen, etcétera.

El primer número real (r1) es un multiplicador, mientras que el segundo (r2) es un desplazamiento de escala aditivo que sólo se utiliza para conversiones de temperatura. La definición de una unidad fundamental permite escribir el nombre de distintas formas (por ejemplo, metro y m) y no importa que esté en mayúsculas o en minúsculas. A continuación se define una unidad fundamental a modo de ejemplo:

*metro(s),metro(s),m
-1,0,1,0,-1,4.1214856408e11,0

En este ejemplo, las constantes que forman un metro son

((1 / c) * h * ( 1 / m)) * (4.1214856 * (10 ^ 11))

Las unidades derivadas se definen en función de otras unidades. Si la línea que sigue a la del nombre de la unidad comienza con un signo igual (=) se trata de una unidad derivada. Los operadores válidos para estas definiciones son * (multiplicación), / (división), + (suma), - (resta) y ^ (exponenciación). Puede hacerse referencia a las unidades predefinidas bien por su nombre o bien por sus abreviaturas (si tienen). Los elementos que componen la fórmula se multiplican todos, a menos que se especifique lo contrario mediante el operador correspondiente. Por ejemplo, la base de datos de unidades define los nombres de múltiplos y submúltiplos sin magnitudes, por lo que pueden especificarse unidades como micropulgadas introduciendo micropulgada. A continuación ofrecemos algunas definiciones de unidades derivadas a modo de ejemplo.


; Unidades de superficie
*township(s)
=93239571.456 meter^2

Se define una ciudad (township) como 93.239.571,456 metros cuadrados. Como vemos, las unidades cuadradas o cúbicas se indican mediante un nombre de unidad definido, el signo de exponenciación y el exponente. Metros cuadrados podría ser: meter^2, m^2, metros^2 y todas las demás combinaciones posibles; metros cúbicos: m^3, metro^3,...

; Unidades electromagnéticas
*voltio(s),v
=vatio/amperio

En este ejemplo se define un voltio como el resultado de dividir un vatio por un amperio. En el archivo ACAD.UNT, tanto los vatios como los amperios están definidos como unidades fundamentales.

Como podemos observar, para incluir comentarios basta con colocar al principio de la línea un punto y coma. El comentario continúa hasta el final de la línea.

 

ONCE.11.1.2. Ejemplos de CVUNIT

Se pueden convertir de unas unidades a otras no sólo valores numéricos sino también valores de punto (listas de dos o tres coordenadas). Veamos algunos ejemplos:

(CVUNIT 180 "degree" "radian") devuelve 3.14159
(CVUNIT 10 "cm" "inch")
devuelve 3.93701
(CVUNIT 25 "celsius" "kelvin")
devuelve 298.15
(CVUNIT 1.25 "horas" "segundos")
devuelve 4500
(CVUNIT 2500 "m^2" "acre")
devuelve 0.617763
(CVUNIT 15 "kg" "libras")
devuelve 33.0693
(CVUNIT ’(2 5 7) "mm" "pulgadas")
devuelve (0.0787432 0.19685 0.275591)
(CVUNIT 760 "grados" "círculo")
devuelve 2.11111

Para la conversión de unidades, AutoLISP necesita acceder cada vez al archivo ACAD.UNT y leer su contenido. Esto resulta asaz lento, por eso, si un programa requiere efectuar una conversión de diversos valores a las mismas unidades, es preferible calcular un factor con CVUNIT para un valor 1 y, después emplear este factor con los demás valores numéricos.

NOTA: Si alguna de las unidades no existe o la conversión resulta incoherente, AutoLISP devuelve nil.

 

9ª fase intermedia de ejercicios

· Realícense diversos ejercicios de conversión de datos y unidades.

· Desarrollar un programa que dibuje una curva helicoidal tridimensional sin grosor mediante una spline. Se indicará el radio inicial, radio final, precisión en puntos en cada vuelta, número de vueltas y paso o altura (se dará para elegir). La curva se generará en el plano XY del SCP actual y alineada con el eje Z.

Autor: Jonathan Préstamo Rodríguez.
Para: La Web del Programador.