3.6. De Coordenadas Universales a Coordenadas de Objeto

por Reinaldo Togores Fernández <reinaldo.togores@unican.es>


  1. Transformación de la Entidad Cabecera ( POLYLINE).
  2. Descripción del programa.
  3. Funciones Auxiliares.
  4. Función principal C:3A2POL.
  5. Conclusiones.

En este tercer artículo abordamos el problema de convertir las coordenadas de puntos extraídas de la lista de datos de entidad de un objeto 3D (en este caso una polilínea 3D), que vienen expresados en valores XYZ referidos al Sistema de Coordenadas Universal (SCU) a valores referidos al Sistema de Coordenadas del Objeto (SCO). La problemática descrita en el primero de los artículos de esta serie ha sido superada en gran medida a partir de la salida al mercado de la versión 14, que discrimina entre splines cuyos vértices son coplanares y aquéllas en que no lo son, para exportarlas en formato versión 12 como Polilíneas 2D ó 3D. Esto no resta, sin embargo, interés al tema de las transformaciones de coordenadas que pueden valer para un gran número de aplicaciones. Y, por supuesto, estamos convencidos de que muchos de nuestros lectores aún siguen utilizando la versión 13. Aunque atendiendo a las ventajas que ofrece la 14, seguramente será por poco tiempo.

Recordemos que aquél programa era solamente válido para polilíneas 3D paralelas al Sistema de Coordenadas Universal (SCU ). El motivo, como explicábamos entonces, es que sólo en este caso coincidirán el SCU (al que se refieren los valores de coordenadas para las entidades 3D) y el SCO (en el que se expresan las coordenadas de los objetos 2D). En aquella ocasión nos interesaba demostrar el procedimiento para manipular las listas de asociación con el propósito de definir entidades empleando la función entmake. La generalización del procedimiento para incluir objetos que presentaran cualquier orientación en el espacio 3D exigía el desarrollo de una serie de funciones auxiliares que fueron el tema de un segundo artículo. Ahora ya podemos abordar las transformaciones de coordenadas de un sistema a otro. Para estas transformaciones habremos de recurrir a la función trans.

La transformación en este caso se hará desde el SCU al SCO, siendo el tipo de dato un punto y no un desplazamiento. Al no tener una entidad a la que recurrir para determinar el SCO, deberemos calcular el vector unitario normal al plano que contiene los vértices, cosa que se logra mediante el uso de las funciones auxiliares ya explicadas en nuestro artículo anterior. Estas funciones realizan además una comprobación previa de la condición de coplanaridad en los vértices de la polilínea 3D original.

El programa de conversión es similar al que se expuso en el primer artículo. Por un momento hemos dudado si desarrollar un nuevo programa que hiciera la conversión directamente al nuevo tipo de POLILÍNEA OPTIMIZADA. Pero nos hemos decidido por mantener el formato de la polilínea clásica sobre la base de dos consideraciones: hacer accesible esta función a los usuarios de la versión 13 y hacer posible la transformación de las polilíneas 3D adaptadas a curva-B (B-spline) que no pueden convertirse a polilíneas optimizadas. Si se quisiera hacer posteriormente la conversión a polilíneas optimizadas bastaría con aplicar la nueva herramienta CONVERTIR que transforma las polilíneas y sombreados de versiones anteriores a las nuevas entidades más compactas de la versión 14.

Recordemos que la polilínea es una secuencia de tres tipos de entidad:

  • una cabecera o entidad POLYLINE,
  • una serie de vértices (entidades VERTEX) que incluyen tanto los que se han introducido por el usuario para definir la entidad como aquéllos que se generan de manera automática al adaptarse la entidad a curva mediante la herramienta EDITPOL.
  • Una entidad SEQEND, que indica el fin de secuencia.

Consideremos cuáles de las sublistas que definen estas entidades se verán afectadas por la transformación de las coordenadas del SCU al SCO. Debemos recordar que todos los cambios que se hacían en el programa original siguen siendo necesarios, y que los descritos a continuación se añaden a aquéllos.

I. Transformación de la Entidad Cabecera (POLYLINE):

Tenemos dos listas de asociación que serán afectadas por las transformaciones:
 

Código 210 Dirección de la extrusión. Expresado como un vector 3D. El valor a utilizar será el mismo calculado mediante la función normal, que además servirá de argumento a la función trans.
Código 10 Según la documentación del programa, un punto "ficticio" construido de manera tal que los valores de X e Y son siempre 0, y el valor de Z es la elevación de la polilínea (en SCO en 2D y SCU en 3D).

La descripción anterior, en lo que se refiere al código 10, puede resultar confusa. Cuando hablamos de entidades 3D, y en particular de la Polilínea 3D, en realidad no tiene sentido referirnos a la "elevación" del objeto. Si estudiamos las listas de asociación que definen la entidad, encontraremos que la lista asociada al código 10 siempre es igual a (10 0.0 0.0 0.0), independientemente del valor asignado a la variable ELEVATION (utilizando el comando ELEV). No sucede así con las polilíneas 2D donde, aunque cada vértice individual está definido por sus coordenadas XYZ, la entidad cabecera también tendrá asignado un valor de "elevación" asociado a este código 10. Una asignación incorrecta de este valor resultará en un desplazamiento de la entidad transformada respecto a la original, aún cuando los valores de las coordenadas de los vértices hayan sido correctamente calculados. El valor que corresponde a la Z del punto ficticio asociado al código 10 lo calcularemos como la distancia entre el plano que pasa por el origen del sistema de coordenadas y el plano que contiene a la entidad. Para ello ha sido necesario desarrollar la nueva función auxiliar pt_elev a partir de las siguientes consideraciones:

Suponiendo que R sea el origen (0,0,0) y P un punto cualquiera de la polilínea, por ejemplo el primer vértice (ver figura 1).


Sea N el vector unitario normal calculado según la función normal que fue desarrollada en el artículo anterior. Podemos definir la distancia entre el plano que contiene al origen y el plano paralelo que contiene a la entidad como la proyección escalar del vector RP (p1i,p2j,p3k) sobre el vector unitario normal N (n1,n2,n3). De manera tal que:
RP·N=p1·n1 + p2·n2 + p3·n3

Véase el listado de esta función más abajo.

Entidades Vértice (VERTEX):

La transformación de SCU a SCO afectará solamente a la lista de asociación que contiene las coordenadas del vértice:
 

Código 10 Punto de ubicación del vértice, expresado en coordenadas referidas al SCO para la polilínea 2D. Este valor se obtendrá de aplicar la función trans al valor del punto referido al SCU (que es el leído de las listas de asociación de la polilínea 3D. La función aplicada se regirá por la siguiente sintaxis: (trans coordenadas_punto 0 vector_normal nil) donde: 
  • el argumento 0 (cero) indica el sistema de coordenadas de origen (SCU ),
  • el argumento vector_normal indica el sistema de coordenadas de destino ( SCO),
  • el argumento nil indica que el valor se refiere a un punto y no a un desplazamiento.

Entidad Fin de Secuencia (SEQEND):

Ninguno de los datos asociados a esta entidad contienen valores expresados en coordenadas XYZ. Por ello en esta entidad no se introduce ninguna transformación adicional.


II. DESCRIPCIÓN DEL PROGRAMA:

Función chk_plano


Función chk_plano:

Esta función viene a sustituir a la función chk_2D del programa original. En esta nueva función se insertan las funciones auxiliares que fueron desarrolladas en nuestro segundo artículo del número anterior, ENTIDADES Y SISTEMAS DE COORDENADAS. La función chk_plano comprueba si una polilínea 3D en cualquier posición del espacio está contenida en un plano. Dicha comprobación sigue el siguiente proceso:

  • Verificar si se trata de una polilínea 3D. En caso afirmativo, se extraerá una lista con las coordenadas de cada vértice (función extpt)
  • la lista de coordenadas se pasa a la función plano que a su vez:
  • extrae mediante la función trio los primeros tres vértices no colineales de la lista (la comprobación del carácter de colinealidad la realiza la función colineal)
  • a partir de estos tres vértices se calcula la fórmula del plano que los contiene (función plano)
  • se evalúa la función para el resto de los puntos de la lista. Si algún punto no satisface la ecuación, la misma evalúa como nil.
  • Si los puntos son coplanares, se calcula (función normal) el valor del vector unitario normal del plano.
(defun chk_plano ( lista_ / resultado)
    ;Comprueba de que se trata de

    ;una Polilínea 3D:
    (if (member '(100 . "AcDb3dPolyline") lista_)
        (if (plano (extpt lista_)) 
            ;Extrae (función extpt) la lista de
            ;vértices y comprueba si son 

            ;coplanares, de serlo calcula
            ;el vector normal…
            (setq resultado (normal A B))
            
            ;en caso de que la entidad no sea una Polilínea 3D 
            ;o que no sea coplanar, se emiten los
            ;correspondientes mensajes de advertencia.
            

            (progn 
                (setq resultado nil)
                (alert "La Polilínea \nNO es coplanar")
            )
        )
        (alert "La entidad seleccionada \nNO es una 3Dpol")

    )
)

III. FUNCIONES AUXILIARES (llamadas desde chk_plano):


Función extpt

Se modifica la anterior función extpt para no incluir los vértices introducidos como puntos de control de una armadura B-spline. Esta comprobación se realiza mediante la función logand, comprobando la existencia del bit 16.

(defun extpt ( lista_ / tmplst )
    (while
        (/= (cdr (assoc 0 
            (setq lista_ (entget (entnext (cdr (assoc -1 lista_)))))

        )) "SEQEND")
        (if (/= (logand 16 (cdr (assoc 70 lista_))) 16)
            (setq tmplst (cons (cdr (assoc 10 lista_)) tmplst))
        )
    ) 
    (setq tmplst (reverse tmplst))
)

Función pt_elev:

Nueva función. Véase la explicación más arriba.


(defun pt_elev ( lista_ vector_normal / tmplst )
  (setq tmplst (extpt lista_))

  (+ 
    (* (car (car tmplst))(car vector_normal))
    (* (cadr (car tmplst))(cadr vector_normal))
    (* (caddr (car tmplst))(caddr vector_normal))
  )
)

Función colineal

Para una explicación detallada ver el artículo anterior

(defun colineal ( ptlist / )
    (setq 
        p1 (car ptlist) p2 (cadr ptlist)
        p3 (caddr ptlist) a1 (- (car p2)(car p1))
        a2 (- (cadr p2)(cadr p1)) a3 (- (caddr p2)(caddr p1))
        b1 (- (car p3)(car p1)) b2 (- (cadr p3)(cadr p1))
        b3 (- (caddr p3)(caddr p1))
    )
    (if (equal (abs (/

        (float(+ (* a1 b1)(* a2 b2)(* a3 b3)))
        (* (sqrt (float (+ (expt a1 2) (expt a2 2) (expt a3 2))))
        (sqrt (float (+ (expt b1 2)(expt b2 2)(expt b3 2)))))
    )) 1 0.00000001) T nil)
)

Función trio

Para una explicación detallada ver el artículo anterior

(defun trio (ptlist / tmplst)
    (setq tmplst ptlist)
    (cond
        ((= (length tmplst) 2)
            (setq p1 (car tmplst) p2 (cadr tmplst)
                p3 (list 
                (1+ (car (last tmplst)))
(cadr (last tmplst))(caddr (last tmplst)))
            )
        )

        ((= (length tmplst) 3)
            (setq p1 (car tmplst) p2 (cadr tmplst) p3 (last tmplst))
        )
        (t 
            (while (and (colineal tmplst) (> (length tmplst) 3))
                (setq tmplst (cdr tmplst))
            )

        )
    )
    (if (colineal (list p1 p2 p3))
        (setq p3 (list (1+ (car p3)) (cadr p3) (caddr p3)))
    )
)

Función plano

Para una explicación detallada ver el artículo anterior

(defun plano ( ptlist / tmplst resultado)
    (trio ptlist)
    (setq 
        A
        (/ 
            (float (- (* (- (caddr p3)(caddr p1))(- (cadr p2)(cadr p1)))
            (* (- (caddr p2)(caddr p1))(- (cadr p3)(cadr p1)))))
            (float (-(* (- (car p2)(car p1))(- (cadr p3)(cadr p1)))

            (* (- (car p3)(car p1))(- (cadr p2)(cadr p1)))))
        )
        B
        (/ 
            (float (-(* (- (caddr p3)(caddr p1))(- (car p2)(car p1)))
            (* (- (caddr p2)(caddr p1))(- (car p3)(car p1)))))
            (float (- (* (- (car p3)(car p1))(- (cadr p2)(cadr p1)))
            (* (- (cadr p3)(cadr p1))(- (car p2)(car p1)))))

        )
        D
        (- (+ (* A (car p1))(* B (cadr p1))(caddr p1)))
    )
    (setq resultado T tmplst ptlist)
    (repeat (length tmplst)
        (if 
            (equal (+ (* A (car (car tmplst)))(* B (cadr (car tmplst)))

            (caddr (car tmplst)) D) 0 0.00000001)
            ( )(setq resultado nil)
        )
        (setq tmplst (cdr tmplst))
    )
    resultado
)

Función normal

Para una explicación detallada ver el artículo anterior

(defun normal ( A B / )
    (list
        (/ A (sqrt (+ (expt A 2) (expt B 2) (expt 1 2))))
        (/ B (sqrt (+ (expt A 2) (expt B 2) (expt 1 2))))
        (/ 1 (sqrt (+ (expt A 2) (expt B 2) (expt 1 2))))
    )
)
)

Función cabecera:

Realiza las modificaciones necesarias a la entidad POLYLINE. A las modificaciones del programa original se añaden la sustitución de los valores asociados a los códigos 10 y 210.

(defun cabecera ( lista_ vector_normal / n_lista cont )
    (setq
        cont 0
        n_lista (quote ())
    )
    (repeat (length lista_)
        (cond             
            ;; Códigos -1 y 5: nombre de la entidad e identificador,
;; Autocad los creará de manera automática por ello no deben
;; incluirse en la lista definitoria 
            ;; de la nueva entidad.
            
            ((or (= (car (nth cont lista_)) -1)
(= (car (nth cont lista_)) 5))())
            
            ;; Se sustituirá, al encontrarlo,
;; el nombre de subclase "AcDb3dPolyline" por 

            ;;el de "AcDb2dPolyline"
            
            ((= (cdr (nth cont lista_)) "AcDb3dPolyline")
                (setq n_lista (cons (cons 100 "AcDb2dPolyline") n_lista))
            )
            
            ;; Código 70: si está establecido el bit 8
;; (comprobado mediante LOGAND), se 

            ;;resta del valor:
            
            ((= (car (nth cont lista_)) 70)
                (if (= (logand (cdr (assoc 70 lista_)) 8) 8) 
                    (setq 
                        n_lista 
                        (cons
(cons 70 (- (cdr (assoc 70 lista_)) 8)) n_lista)
                    )

                )
            )
            
            ;; Código 210: Sustituirlo por el vector normal del plano que
;; contiene la polilínea:
            
            ((= (car (nth cont lista_)) 210)
                (setq n_lista (cons (cons 210 vector_normal) n_lista))
            )
            

            ;; Código 10: Elevación: calculada a partir de la distancia
;; desde el origen al plano:
            
            ((= (car (nth cont lista_)) 10)
                (setq 
                    n_lista 
                    (cons 
                        (cons 10
(list 0.0 0.0 (pt_elev lista_ vector_normal))) 

                        n_lista
                    )
                )
            )
            
            ;;y las demás sublistas se transfieren sin modificación:
            (t (setq n_lista (cons (nth cont lista_) n_lista)))
        )

        (setq cont (1+ cont))
    )
    
    ;;invierte la lista y crea la cabecera de la polilínea
    ;;en caso de que la operación no tenga éxito, se establece
;;la variable p_err.
    
    (if (not (entmake (reverse n_lista)))(setq p_err t))
)

Función vertices:

(defun vertices ( lista_ vector_normal / )
    (while 
        (/= 
            (cdr 
                (assoc 0 

                    (setq lista_ (entget (entnext (cdr (assoc -1 lista_)))))
                )
            )
            "SEQEND"
        )
        (setq cont 0 n_lista (quote ()))
        (repeat (length lista_)
            (cond
                ;;Códigos -1 y 5: nombre de la entidad e identificador,
;;se suprimen.
                
                ((or (= (car (nth cont lista_)) -1)
                     (= (car (nth cont lista_)) 5)) 
                    ()
                ) 
                
                ;;Se sustituirá, al encontrarlo,
;;el nombre de subclase "AcDb3dPolylineVertex" 

                ;;por el de "AcDb2dVertex"
                
                ((= (cdr (nth cont lista_)) "AcDb3dPolylineVertex")
                    (setq n_lista (cons (cons 100 "AcDb2dVertex") n_lista))
                )
                
                ;;Código 70: si está establecido el bit 32, se elimina.

                
                ((= (car (nth cont lista_)) 70)
                    (if (= (logand (cdr (assoc 70 lista_)) 32) 32) 
                        (setq 
                            n_lista 
                            (cons (cons 70
(- (cdr (assoc 70 lista_)) 32)) n_lista)
                        )
                    )

                )
                
                ;; Código 10: se transforman las coordenadas SCO en SCU,
                ;; utilizando para ello la función TRANS.
                
                ((= (car (nth cont lista_)) 10)
                    (setq 
                        n_lista 

                        (cons 
                            (cons 10
(trans (cdr (assoc 10 lista_))
0 vector_normal nil))
                            n_lista
                        )
                    )
                )
                
                ;;y las demás sublistas se copian sin cambios
                
                (t
                    (setq n_lista (cons (nth cont lista_) n_lista))
                )
            )
            (setq cont (1+ cont))
        )
        (if (not (entmake (reverse n_lista)))(setq v_err t))
    )

    
    ;;La variable lista_ contiene ahora la lista de asociación de
;;la entidad SEQEND. 
    ;;Se procederá a crear ahora la nueva entidad SEQEND
    
    (setq cont 0 n_lista (quote ()))
    (repeat (length lista_)
        (cond
            

            ;;Es necesario suprimir el código -2,
;;nombre de la entidad cabecera. 
            ;;Este código será asignado por AutoCAD.
            
            (
                (or 
                    (= (car (nth cont lista_)) -1)

                    (= (car (nth cont lista_)) -2)
                    (= (car (nth cont lista_)) 5)
                ) 
                ()
            ) 
            (t
                (setq n_lista (cons (nth cont lista_) n_lista))
            )

        )
        (setq cont (1+ cont))
    )
    (reverse n_lista)
    (if (not (entmake (reverse n_lista)))(setq s_err t))
    
    ;;Antes de borrar el objeto original se comprueba 
    ;;que no existan errores en la creación de 

    ;;las entidades componentes.
    
    (if (or p_err v_err s_err)
        (alert 
            "Error en la \ncreación de entidades.
\nNO se borrará el original."
        )
        (entdel (cdr (assoc -1 lista_ent)))

    )
)

IV. Función principal C:3A2POL:

(defun C:3A2POL ( / p_err v_err s_err lista_ent prev oce A B D
ptlist p1 p2 p3)
    (setq oce (getvar "cmdecho"))
    (setvar "cmdecho" 0)

    
    ;;Selección de la entidad a convertir.
    ;;Para selecciones múltiples crear un 
    ;;conjunto de selección con SSGET
    
    (setq lista_ent (entget (car(entsel))))
    
    ;;Comprueba que sea una 3Dpol, y si lo es 

    ;;comprueba que sea coplanar.
    ;;si la función chk_plano NO devuelve nil, 
    ;;entonces realiza la transformación.
    
    (if (setq vector_normal (chk_plano lista_ent))
        (progn
            (cabecera lista_ent vector_normal)
            (vertices lista_ent vector_normal)

        )
    )
    (setvar "cmdecho" oce)
    (princ)
)
(prompt "\n\t3A2: Conversión de Polilíneas 3D a 2D, versión 2")(princ)

V. CONCLUSIONES:

Completamos con este tercer artículo una investigación en torno los mecanismos empleados por AutoCAD para la definición de entidades mediante la escritura directa a la base de datos del dibujo a través de la función entmake. Como dijimos en el primero de esta serie, dicho procedimiento tiene como ventajas sobre la creación de entidades mediante la función command una independencia total de los ajustes establecidos en materia de sistemas de coordenadas, referencias a entidades, elevación, etc. así como una limpieza y rapidez de ejecución que se evidencian en el presente programa. Pero con esto no se agotan las aplicaciones posibles de estos procedimientos basados en la función entmake. Como muestra de las posibilidades aún no tratadas podemos mencionar la creación de bloques de cualquier complejidad directamente por el mismo programa que los inserta, sin tener que disponer previamente de una biblioteca externa, o la creación por el mismo procedimiento de los objetos no – gráficos del dibujo.

 


Reinaldo Togores Fernández, Arquitecto
Profesor asociado de Técnicas de Expresión Gráfica,
Escuela Técnica Superior de Ingenieros de Caminos, Canales y Puertos, Universidad de Cantabria
Instructor de CAD del Instituto de Formación y Estudios Sociales.

ċ
3a2pol-es.lsp
(7k)
Reinaldo Togores Fernández,
28 mar. 2011 2:25