Nuestro artículo anterior
refería la manera en que Visual LISP es capaz de gestionar otras aplicaciones
del entorno Windows. Continuamos ahora demostrando cómo es capaz también de
acceder a la propia jerarquía de objetos de AutoCAD, gestionando sus métodos y
propiedades.
El trabajar con bloques aporta eficacia al uso de un
sistema CAD. En lugar de dibujar cada vez una entidad gráfica que se repite,
podemos empaquetar sus componentes en un único objeto contenedor que se referencia
desde cada uno de sus inserciones en el dibujo. Es decir que lo que aparece en
cada ocurrencia del bloque es como una vista del paquete original,
desplazada al lugar de la inserción, posiblemente girada y en algunos casos
sometida a transformaciones de escala.
El circulo, el arco, la línea que vemos no es un objeto
nuevo y distinto, sino una referencia a la definición contenida en el
objeto Bloque original. De aquí que debamos distinguir entre dos objetos
diferentes:
- el objeto Definición de Bloque
- el objeto Referencia de Bloque
El segundo es una copia geométricamente transformada
del primero.
Un texto puede formar parte de un bloque, como cualquiera
otra entidad gráfica. Pero en muchos casos es deseable que el texto de un
bloque pueda cambiar para cada una de sus referencias en el
dibujo.
La posibilidad de incluir como parte de una definición de
bloque información alfanumérica variable para cada ocurrencia constituye una
anomalía respecto a la definición estricta de bloque. Ya no se trataría de una
simple copia de la definición original.
Para dar solución a este caso se recurre a un proceso
similar al utilizado para generar el bloque mismo: se crea una entidad gráfica
especial, la entidad Definición de Atributo, que actúa como plantilla,
reservando un lugar y predefiniendo las características visuales que adoptará
dicha información.
Esta viene a concretarse en su contenido literal sólo en el
momento de generarse cada ocurrencia particular, es decir en el momento de la
inserción del bloque, pasando a generar una entidad nueva, el objeto Referencia
de Atributo, asociado siempre al objeto Referencia de Bloque. La Referencia
de Atributo se asocia a la Referencia de Bloque estableciendo una
secuencia en la base de datos de entidades, de manera tal que este último
objeto actúa como cabecera de una sucesión de Referencias de Atributo
que viene a concluir con la entidad especial Fin de Secuencia. El
comando DBLIST aplicado en un
dibujo con bloques insertados permite comprobar este aspecto.
Acceso a los Atributos
El mecanismo habitual para recuperar los atributos desde
AutoLISP ha sido el de recorrer linealmente la base de datos gráfica, partiendo
de la entidad INSERT (Referencia
de Bloque) mediante la función entnext
hasta encontrar la entidad SEQEND
(Fin de Secuencia). La Figura 1 muestra, a manera de ejemplo, el código
de una función de este tipo.
Figura 1 Función recursiva LISP para extraer Atributos
El entorno ActiveX exige proceder de
distinta manera. Los objetos Referencia de Atributo se han de recuperar
mediante un método especializado del objeto Referencia de Bloque.
Al recorrer las colecciones Blocks, ModelSpace o PaperSpace, pueden aparecer
objetos Definición de Atributo, pero dichos objetos son la plantilla
para los atributos efectivamente insertados en el dibujo, y como tales, no
contendrán los valores que a ellos se ha asignado en el momento de la
inserción.
El método que permite acceder a los
valores de los atributos de una Referencia de Bloque es el método GetAttributes()
del
objeto AcadBlockReference
.
Este método devuelve un objeto de tipo VARIANT que
contiene una matriz en la cual cada término apunta a una de las Referencias
de Atributo del bloque.
Es decir, que en primer lugar debemos
llegar al objeto Referencia de Bloque. Hacerlo recurriendo a la
jerarquía de objetos que expone la Interface ActiveX de AutoCAD mediante la
programación VBA o Visual Basic exigiría partir desde lo más alto de esta
jerarquía, el objeto Aplicación, para desde ahí ir descendiendo hasta
llegar a los objetos que nos interesan, todos los cuales forman parte de Colecciones,
las Definiciones de Bloque a la colección Blocks, y las Referencias
de Bloques y de Atributos a las colecciones ModelSpace o PaperSpace.
Desde Visual LISP, sin embargo, podemos acceder directamente
al objeto a partir de su ENAME
o
nombre de entidad, devuelto por funciones como entsel
o desde conjuntos de selección creados con ssget
.
Selección de los Bloques a Procesar
Estas funciones AutoLISP exigen muchos menos pasos que las
equivalentes ActiveX, por lo que partimos de un conjunto de selección creado de
la manera más tradicional, tal como se demuestra en la Figura 2.
Una vez creado el conjunto de selección
mediante la función ssget a la que se aportan como filtros la condición de ser una entidad
INSERT y el nombre del bloque cuyos datos se desea extraer:
(ssget "X" (list (cons 0
"INSERT")(cons 2 nombre)))
lo recorreremos utilizando como índice la variable cont
, inicializada a cero. Para poder
utilizar los procedimientos ActiveX, deberemos obtener, a partir del nombre de
entidad que se recupera del conjunto de selección mediante la función ssname
,
una referencia al objeto correspondiente. Esto se logra mediante la función vlax-ename->vla-object
. La
referencia devuelta por esta función se pasa directamente a la función LeeAtribX
que es la encargada de
extraer la información deseada y estructurarla en forma de lista.
Figura 2 Rutina que solicita el nombre del Bloque a
seleccionar
Extracción de la Información del Bloque
Los objetos ActiveX poseen propiedades, que contienen
información equivalente a la que se obtiene a partir de la lista de datos de
entidad devuelta por entget
. De
los datos a exportar, algunos los obtendremos a partir de leer propiedades de
la Referencia de Bloque. En primer lugar debemos comprobar si el bloque posee atributos.
Este dato corresponde a la propiedad HasAttributes
.
Esta propiedad puede ser cierta :vlax-true
o falsa :vlax-false
. Para
obtener una propiedad disponemos de la función vlax-get-property.
La extracción de datos continuará siempre que se cumpla la condición:
(equal (vlax-get-property objBloque
"HasAttributes") :vlax-true)
Los datos que hemos elegido exportar son los siguientes:
·
Nombre del Bloque
·
Identificador y Valor de cada Atributo
·
Coordenadas de la esquina inferior izquierda de la caja de
abarque del bloque
·
Coordenadas de la esquina superior derecha de la caja de abarque
del bloque
Toda vez que la lista se construirá mediante la sucesiva
aplicación de la función cons
que añade un primer término a una lista, el orden de extracción de estos datos
será al revés de cómo queremos la lista definitiva. Es decir que comenzaremos
por las coordenadas de la caja de abarque. La Figura 3 muestra el código
de la función que extrae la información y prepara la lista.
Coordenadas de la Caja de Abarque
Aquí se trata ya no de una propiedad, sino de un método
que poseen todos los objetos gráficos y que lo heredan del antecesor común AcadObject.
Un método no equivale a un dato. Es más bien comparable con una rutina que al
recibir determinados parámetros realiza una determinada operación sobre un
objeto. Tal operación puede ser la de calcular, como en el caso que nos ocupa,
los extremos de su caja de abarque.
El método que hemos de aplicar para ello es el GetBoundingBox ()
. Visual LISP
proporciona una serie de funciones que sirven como envoltorios para los
métodos ActiveX. Estas funciones no las encontraremos en la documentación de
Visual LISP. En cambio, el Entorno de Desarrollo (IDE) nos brinda una
herramienta que resulta imprescindible en estos casos. Nos referimos a la
utilidad APROPOS. Abramos la ventana APROPOS (Ctrl-Shift-A) y tecleemos vla-get
en la casilla de edición. Al
pulsar OK se abrirá una ventana donde se aprecian los nombres de las 560
funciones disponibles que incluyen vla-get
.
Figura 3 Función que extrae datos del Bloque

Figura 4 Resultados de la búsqueda por APROPOS
|
Si seleccionamos vla-GetBoundingBox ,
y pulsamos sobre el icono de ayuda en la ventana APROPOS RESULTS accederemos
a la Referencia ActiveX y VBA de AutoCAD. Ahí comprobaremos que este método
requiere dos argumentos adicionales que no son otros que los nombres de las
variables en que deseamos guardar los puntos devueltos.
La sintaxis según la Referencia ActiveX-VBA sería:
object.GetBoundingBox
MinPoint, MaxPoint
que traducimos a Visual LISP como:
(vla-GetBoundingBox
objBloque 'minimo 'maximo)
Otro paso necesario será la conversión del tipo de dato
devuelto al tipo de dato de punto usual en AutoLISP. El tipo devuelto es un
VARIANT que contiene una matriz de tres números de coma flotante de doble
precisión.
Los puntos aparecerán como cola -cdr- de una lista, cuya cabeza servirá como
identificador. En este caso los identificadores los suministramos como una
cadena de texto:
(cons
"ExtMax" (list (vlax-safearray->list maximo)))
que devolvería algo como:
("ExtMax"
(413986.0 4.79686e+006 0.0))
|
La necesidad de esta conversión será tan frecuente que
Visual LISP suministra una función para ello:
(vlax-safearray->list
maximo)
Valores de los Atributos
Otra herramienta que nos permite profundizar en el conocimiento
de los objetos ActiveX es INSPECT (Ctrl-Shift-I). Inspect nos permite
introducir un nombre de variable que contenga la referencia a un objeto
ActiveX, o simplemente seleccionarla con el cursor para abrir una ventana de
inspección donde aparecerán realcionadas todas sus propiedades. Proponemos
teclear en la consola Visual LISP:
(setq objBloque
(vlax-ename->vla-object (car(entsel))))
y seleccionar un bloque en el dibujo. Aplicando la
herramienta INSPECT sobre el símbolo objBloque inspeccionaremos sus propiedades.
Los atributos se obtendrán mediante el método GetAttributes ()
(setq atributos
(vla-GetAttributes objBloque))
La Figura 5 muestra los resultados de aplicar la herramienta
INSPECT sobre la variable atributos
y los resultados de sucesivos doble clics sobre los conceptos que aparecen en
las ventanas resultantes.
Figura 5 Inspección de los objetos ActiveX
El método GetAttributes()
devuelve un objeto de
tipo VARIANT que contiene una matriz en la cual cada elemento apunta a un
objeto Referencia de Atributo.
La recuperación de dichos valores exige
una doble transformación.
Los datos de tipo matriz devueltos por
los métodos ActiveX están contenidos en VARIANTS. Primero habrá que recuperar el valor del dato desde el tipo VARIANT. Para ello
empleamos vlax-variant-value
que nos devuelve su contenido que en este caso será una matriz en
formato safearray
. Entonces utilizaremos vlax-safearray->list
que nos devolverá un dato de tipo lista, procesable desde LISP.
Como no sabemos de antemano la cantidad
de elementos de que se compone la matriz recurriremos a la función LISP foreach
, que es
capaz de procesar cada término de la lista, no importa cuántos sean. Cada
término de la lista apuntará a un objeto Referencia de Atributo, del que
obtendremos los valores deseados mediante la función Procesa
(ver Figura
6), que recibe como argumento la referencia a un objeto-VLA que a su vez
referencia un Atributo.
Figura 6 Función que recupera los valores de los Atributos
Esta función se limita a crear un par
punteado con las cadenas que representan el identificador y el valor del
atributo.
Para terminar, se extraerá el nombre del
bloque a partir de la propiedad Name del objeto Referencia de Bloque:
(vlax-get-property
objBloque "Name")
De la ejecución de este programa resulta una lista que a su
vez contiene una sublista para cada bloque. La sublista del bloque a su vez
contiene pares de datos estructurados como cabeza y cola de la lista.
((("Bloque" .
"FINCA")
("PROVINCIA" .
"39")
("AYUNTAMIENTO" .
"087")
("PUEBLO" .
"0010")
("CALLE" .
"0190")
("PORTAL" .
"0012")
("PORT-BIS" .
"51")
("ESCALERA" .
"00")
("ALTURAS" .
"0001")
("ExtMin" (413981.0
4.79685e+006 0.0))
("ExtMax" (413986.0
4.79686e+006 0.0))
))
Conclusiones
Gran parte de la información que contiene esta lista pudiera
haber sido extraída a partir de una función puramente LISP como la que se
muestra en la Figura 1. Sin embargo, la información que se refiere a la caja
de abarque hubiera debido ser obtenida mediante una función programada a
partir de cero, y de una manera que tuviera en cuenta los diversos objetos
gráficos a los que se pudiera aplicar.
En un entorno de Programación Orientada a objetos, sin
embargo, un método como el GetBoundingBox
()
es heredado por todos los objetos gráficos a partir de su antecesor
común AcadObject
lo que nos
permite obtener este dato de manera inmediata. Y esto sin renunciar a las
ventajas de la programación LISP. Digamos que "lo mejor de ambos
mundos".
Para terminar, los lectores de nuestro anterior artículo observarán
que el formato de la lista resultante es exactamente la que requiere la función
Lista->Excel
allí reseñada.
De manera que con esto completaríamos un programa capaz de extraer información
de los bloques de un dibujo de AutoCAD para exportarlos a una tabla de Excel.