Vous êtes sur la page 1sur 71

Introduo Programao com AutoLisp

Antnio Menezes Leito Fevereiro 2007

Contedo
1 Listas 1.1 Tipos Recursivos . . . . . . . . . 1.2 Recurso em Listas . . . . . . . . 1.3 Enumeraes . . . . . . . . . . . . 1.4 Comparao de Smbolos e Listas 1.5 Representao Grca de Listas . 1.6 Comparao de Listas . . . . . . 1.7 Comparao de Smbolos . . . . 1.8 Comparao Estrutural de Listas Carregamento de Ficheiros 2.1 Carregamento de Dependncias . 2.2 Mdulos . . . . . . . . . . . . . . 2.3 Carregamento Automtico . . . . 2.4 Interpretao e Compilao . . . Listas de Coordenadas 3.1 Polgonos . . . . . . . . . . . 3.1.1 Estrelas Regulares . 3.1.2 Polgonos Regulares 3.2 Iterao em Listas . . . . . . 3.3 Linhas Poligonais e Splines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 3 4 8 10 10 11 12 12 14 15 16 20 21 23 23 25 27 28 30 35 36 39 39 40 42 49 50 50 50 51 54 54 56 57 58 59 59 60

Manipulao de Entidades 4.1 Listas de Associaes . . . . . . . . . . . . . 4.2 A funo assoc . . . . . . . . . . . . . . . . 4.3 Modicao de Entidades . . . . . . . . . . 4.4 Criao de Entidades . . . . . . . . . . . . . 4.5 Listas de Propriedades . . . . . . . . . . . . 4.6 Leitura e Modicao de Entidades . . . . . 4.7 Eliminao de Entidades . . . . . . . . . . . 4.8 ltima Entidade . . . . . . . . . . . . . . . . 4.9 Iterao de Entidades . . . . . . . . . . . . . 4.10 Entidades Compostas . . . . . . . . . . . . . 4.11 Redesenho de Entidades . . . . . . . . . . . 4.12 Criao de Linhas Poligonais Leves . . . 4.13 Manipulao de Linhas Poligonais Leves 4.14 Entidades Degeneradas . . . . . . . . . . 4.15 Seleco Interactiva de Entidades . . . . . . 4.16 Denio de Comandos AutoCad . . . . . . 4.17 Obteno de Informao . . . . . . . . . . . 4.18 Exemplos . . . . . . . . . . . . . . . . . . . .

Listas

Referimos anteriormente que uma lista uma sequncia de elementos. Vimos que a funo car devolve o primeiro elemento da lista e a funo cdr devolve o resto da lista, i.e., a lista aps o primeiro elemento. Por exemplo:
_$ (car (list 1 2 3 4)) 1 _$ (cdr (list 1 2 3 4)) (2 3 4)

Se as funes car e cdr permitem decompor uma lista, a funo cons faz o inverso: dado um elemento e uma lista, devolve uma nova lista idntica anterior mas com o elemento no princpio. Por exemplo:
_$ (cons 1 (list 2 3 4)) (1 2 3 4)

Finalmente, vimos tambm que o Lisp considera que o smbolo nil representa uma lista vazia. De facto, quando tentamos obter o resto de uma lista com um nico elemento, obtemos nil, tal como quando tentamos construir uma lista sem elementos:
_$ (cdr (list 1)) nil _$ (list) nil

A lista vazia reconhecvel pela funo null, que devolve verdade quando o argumento a lista vazia nil e falso caso contrrio:
_$ (null (list)) T _$ (null (list 1 2)) nil

Embora o Lisp use o smbolo nil para representar uma lista vazia, sabemos que este smbolo tambm usado para representar a falsidade, tal como a ltima expresso demonstra. Embora internamente seja exactamente a mesma coisa, para distinguir os dois usos o Lisp permite uma notao diferente para o smbolo nil: (). Isso visvel na seguinte interaco:
_$ () nil _$ (null ()) T

A lista vazia () o ponto de partida para a construo de qualquer lista, tal como podemos ver na seguinte sequncia de expresses:

_$ (cons 3 ()) (3) _$ (cons 2 (cons 3 ())) (2 3) _$ (cons 1 (cons 2 (cons 3 ()))) (1 2 3)

Repare-se, nas expresses anteriores, que o segundo argumento da funo cons , ou uma lista vazia, ou uma lista contendo alguns elementos. Nestes casos, o resultado da invocao da funo cons ser sempre uma lista. Obviamente, qualquer que seja a lista que imaginemos, sempre possvel constru-la usando apenas invocaes da funo cons e a lista vazia (). Consequentemente, podemos dizer que uma lista sempre: ou a lista vazia (), ou o cons de um elemento a uma lista. H um detalhe subtil na denio de lista que acabmos de apresentar: a denio recursiva! Para o conrmar basta reparar que estamos a denir uma lista como o cons de um elemento a uma lista, i.e., usamos o termo que queremos denir na prpria denio. Como j sabemos de qualquer denio recursiva, necessrio termos um caso de paragem que, no caso das listas, a lista vazia. Esta caracterstica recursiva das listas emerge em inmeras ocasies e, na verdade, permite denir as operaes sobre listas com muito mais rigor do que possvel quando esta caracterstica no explorada. Por exemplo, aquando da explicao da funo list (Pgina ??) dissemos que uma expresso da forma
(list e1 e2 ... en )

era equivalente a
(cons e1 (cons e2 (... (cons en nil)...)))

Ora esta equivalncia , na verdade, muito pouco rigorosa pois depende da inteligncia de quem a l para adivinhar que cada uma daquelas reticncias designa uma expresso diferente. No entanto, explorando as propriedades recursivas das listas, podemos inventar uma denio recursiva para a operao que substancialmente mais rigorosa: (list) equivalente a () e (list e1 e2 ... en ) equivalente a (cons e1 (list e2 ... en )). Notese que, agora, as reticncias representam exactamente a mesma coisa nas duas expresses, pelo que a possibilidade de m interpretao muito menor.

1.1

Tipos Recursivos

Quando um tipo de dados denido de forma recursiva dizemos que temos um tipo recursivo. As listas so, portanto, um tipo recursivo. Num tipo recursivo tem de existir sempre um elemento primordial, a partir do qual se criam 3

os restantes elementos. A esse elemento primordial chama-se elemento primitivo. No caso das listas, o elemento primitivo , obviamente, a lista vazia. Tal como acontecia com os outros tipos de dados, as vrias operaes que permitem manipular listas podem classicar-se em construtores, selector e reconhecedores. fcil ver que () e cons so construtores, car e cdr so selectores e, nalmente, null o reconhecedor. Usando estas operaes podemos agora denir inmeras outras operaes sobre listas.

1.2

Recurso em Listas

Uma das propriedades interessantes dos tipos recursivos que todas as operaes que processam elementos do tipo tendem a ser implementadas por funes recursivas. Por exemplo, se quisermos denir uma funo que nos diz quantos elementos existem numa lista, temos de pensar recursivamente: Se a lista vazia, ento o nmero de elementos , obviamente, zero. Caso contrrio, o nmero de elementos ser um mais o nmero de elementos do resto da lista. Para nos assegurarmos da correco do nosso raciocnio temos de garantir que se vericam os pressupostos a que todas as denies recursivas tm de obedecer, nomeadamente: Que h uma reduo da complexidade do problema a cada invocao recursiva: de facto, a cada invocao recursiva a lista usada mais pequena. Que h um caso mais simples de todos onde existe uma resposta imediata: quando a lista vazia, a resposta zero. Que existe uma equivalncia entre o problema original e o uso do resultado da recurso: verdade que o nmero de elementos de uma lista o mesmo que somar 1 ao nmero de elementos dessa lista sem o primeiro elemento. A vericao destes pressupostos suciente para garantirmos que a funo est correcta. Traduzindo este raciocnio para Lisp, obtemos:
(defun numero-elementos (lista) (if (null lista) 0 (+ 1 (numero-elementos (cdr lista)))))

Experimentando, temos:
_$ (numero-elementos (list 1 2 3 4)) 4 _$ (numero-elementos (list)) 0

importante percebermos que a presena de sublistas no altera o nmero de elementos de uma lista. De facto, os elementos das sublistas no so considerados elementos da lista. Por exemplo:
_$ (list (list 1 2) (list 3 4 5 6)) ((1 2) (3 4 5 6)) _$ (numero-elementos (list (list 1 2) (list 3 4 5 6))) 2

Como se v no exemplo anterior, a lista apenas contm dois elementos, embora cada um deles seja uma lista com mais elementos. Na verdade, a funo numero-elementos j existe em Lisp com o nome length. Uma outra operao til aquela que nos permite obter o n-simo elemento de uma lista. pragmtica usual assumir-se que, para n = 0, se deve obter o primeiro elemento da lista. Para denir esta funo devemos, mais uma vez, pensar de forma recursiva: Se n zero, devolvemos o primeiro elemento da lista. Caso contrrio, devolver o n-simo elemento da lista o mesmo que devolver o (n 1)-simo elemento do resto da lista. Novamente vericamos que h uma reduo do problema, que a reduo equivalente ao problema original e que o problema se vai aproximando do caso bsico. Em Lisp, temos:
(defun n-esimo (n lista) (if (= n 0) (car lista) (n-esimo (- n 1) (cdr lista)))) _$ (n-esimo 2 (list 0 1 2 3 4)) 2

Na verdade, a funo n-esimo j existe em Lisp com o nome nth.


Exerccio 1.2.1 Escreva uma funo muda-n-esimo que recebe um nmero n, uma lista e um elemento, e substitui o n-simo elemento da lista por aquele elemento. Note que o primeiro elemento da lista corresponde a n igual a zero. Por exemplo:
_$ (muda-n-esimo 2 (list 0 1 2 3 4 5) 9) (0 1 9 3 4 5) _$ (muda-n-esimo 2 (list "Vou" "para" "Coimbra") "Lisboa") ("Vou" "para" "Lisboa")

Exerccio 1.2.2 Escreva uma funo que dada uma lista de elementos devolve um elemento dessa listas, escolhido aleatoriamente.

frequentemente necessrio termos de concatenar listas. Para isso, podemos imaginar uma funo concatena que, dadas duas listas, devolve uma lista contento, pela mesma ordem, os elementos das duas listas. Por exemplo: 5

_$ (concatena (list 1 2 3) (list 4 5 6 7)) (1 2 3 4 5 6 7)

Como sempre, a recurso ajuda a resolver o problema. Comeemos por pensar na simplicao do problema, i.e., em transformar o problema original (concatena (list 1 2 3) (list 4 5 6 7)) num problema ligeiramente mais simples. Uma vez que h duas listas como parmetros, podemos simplicar o problema fazendo uma reduo numa delas, i.e., podemos considerar uma reduo na primeira lista
(concatena (list 2 3) (list 4 5 6 7))

cujo resultado (2 3 4 5 6 7), ou, alternativamente, uma reduo na segunda lista


(concatena (list 1 2 3) (list 5 6 7))

cujo resultado (1 2 3 5 6 7). Para alm da simplicao do problema, tambm necessrio garantir que conseguimos arranjar uma maneira de fazer com que a simplicao do problema seja equivalente ao problema original. Para a primeira alternativa isso trivial, basta inserir o nmero 1 cabea da lista, i.e., fazer
(cons 1 (concatena (list 2 3) (list 4 5 6 7)))

Para a segunda alternativa isso bastante mais difcil pois teriamos de inserir o nmero 4 algures no meio da lista resultante. Assim sendo, no devero restar muitas dvidas que a funo dever ter a forma:
(defun concatena (l1 l2) (if ??? ??? (cons (car l1) (concatena (cdr l1) l2))))

Falta agora tratar o caso bsico. Para isso, basta pensar que se estamos a reduzir a primeira lista a cada invocao recursiva ento chegar um momento em que a primeira lista ca vazia. O que ento a concatenao de uma lista vazia a uma outra lista qualquer? Obviamente, a outra lista. Assim, podemos nalmente denir completamente a funo:
(defun concatena (l1 l2) (if (null l1) l2 (cons (car l1) (concatena (cdr l1) l2))))

Na verdade, esta funo j existe em Lisp com o nome append e tem a vantagem adicional de receber qualquer nmero de argumentos, por exemplo: 6

_$ (append (list 0) (list 1 2 3) (list 4 5) (list 6 7 8 9)) (0 1 2 3 4 5 6 7 8 9)

Para terminar, vamos considerar uma ltima funo que obtm uma sublista a partir de uma lista dada. Essa sublista inclui todos os elementos entre dois ndices dados. Usualmente, convenciona-se que a sublista inclui o elemento com o primeiro ndice e exclui o elemento com o segundo ndice. Como exemplo, temos:
_$ (sublista (list 0 1 2 3 4 5 6) 2 4) (2 3)

Para denirmos esta funo, podemos recursivamente simplicar o problema da obteno da sublista de (e0 e1 ... en ) entre os ndices i e j para passar a ser a obteno da sublista de (e1 ... en ) entre os ndices i 1 e j 1. Quando i for zero, ento passamos a transformar o problema da obteno da sublista de (e0 e1 ... en ) entre os ndices 0 e j para passar a ser o cons de e0 com a sublista de (e1 ... en ) entre os ndices 0 e j 1. Traduzindo este algoritmo para Lisp, temos:
(defun sublista (lista inicio fim) (cond ((> inicio 0) (sublista (cdr lista) (1- inicio) (1- fim))) ((> fim 0) (cons (car lista) (sublista (cdr lista) inicio (1- fim)))) (t ()))) Exerccio 1.2.3 Como vimos, a funo cons acrescenta um novo primeiro elemento a uma lista, a funo car devolve o primeiro elemento de uma lista e a funo cdr devolve a lista sem o primeiro elemento. Escreva as funes inversas do cons, car e cdr, designadas snoc, rac e rdc que, ao invs de operarem com o primeiro elemento, operam com o ltimo. O snoc recebe um elemento e uma lista e junta o elemento ao m da lista. O rac devolve o ltimo elemento da lista. O rdc devolve uma lista com todos os elementos menos o ltimo. Exerccio 1.2.4 Dena uma funo inverte que recebe uma lista e devolve outra lista que possui os mesmos elementos da primeira s que por ordem inversa. Exerccio 1.2.5 Sendo as listas uma estrutura de dados to exvel, muito frequente usarmos listas contendo outras listas que, por sua vez, podem conter outras listas, at ao nvel de profundidade que se queira. Por exemplo, consideremos a seguinte lista de posies:
_$ (list (list 1 2) (list 3 4) (list 5 6)) ((1 2) (3 4) (5 6))

ou a seguinte lista de listas de posies:


_$ (list (list (list 1 2) (list 3 4) (list 5 6)) (list (list 7 8) (list 9 0))) (((1 2) (3 4) (5 6)) ((7 8) (9 0)))

Escreva uma funo denominada alisa que recebe uma lista (possivelmente com sublistas) como argumento e devolve outra lista com todos os tomos da primeira e pela mesma ordem, i.e.
_$ (alisa (1 2 (3 4 (5 6)) 7)) (1 2 3 4 5 6 7)

Exerccio 1.2.6 Escreva uma funo que calcula o nmero de tomos que uma lista (possivelmente com sublistas) tem.

1.3

Enumeraes

Vamos agora considerar uma funo que ser til no futuro para criar enumeraes. Dados os limites a e b de um intervalo [a, b] e um incremento i, a funo dever devolver uma lista com todos os nmeros a, a + i, a + 2i, a + 3i, a + 4i,. . . ,at que a + ni > b. Mais uma vez, a recurso ajuda: a enumerao dos nmeros no intervalo [a, b] com um incremento i exactamente o mesmo que o nmero a seguido da enumerao dos nmeros no intervalo [a + i, b]. O caso mais simples de todos uma enumerao num intervalo [a, b] em que a > b. Neste caso o resultado simplesmente uma lista vazia. A denio da funo agora trivial:
(defun enumera (a b i) (if (> a b) () (cons a (enumera (+ a i) b i))))

Como exemplo, temos:


_$ (1 _$ (1 (enumera 1 5 1) 2 3 4 5) (enumera 1 5 2) 3 5)

Para tornarmos a funo ainda mais genrica, podemos tratar tambm o caso em que o incremento d negativo. Isto til, por exemplo, para uma contagem regressiva: (enumera 10 0 -1). Note-se que a denio actual da funo enumera no permite isso pois a utilizao de um incremento negativo provoca recurso innita. claro que o problema est no facto do teste de paragem ser feito com a funo < que s apropriada para o caso em que o incremento positivo. No caso de incremento negativo deveriamos usar >. Assim, para resolvermos o problema temos de identicar primeiro qual a funo correcta a usar para a comparao, algo que podemos fazer com um simples teste:
(defun enumera (a b i) (if ((if (> i 0) > <)

a b) () (cons a (enumera (+ a i) b i))))

Agora j podemos ter:


_$ (1 _$ (5 _$ (6 (enumera 1 5 1) 2 3 4 5) (enumera 5 1 -1) 4 3 2 1) (enumera 6 0 -2) 4 2 0)

Exerccio 1.3.1 Infelizmente, a funo enumera desnecessariamente ineciente pois, apesar do incremento nunca mudar ao longo das invocaes recursivas, estamos sistematicamente a testar se ele positivo. Obviamente, basta testar uma nica vez para se decidir, de uma vez por todas, qual o teste que se deve fazer. Redena a funo enumera de modo a que no sejam feitos quaisquer testes desnecessrios. Exerccio 1.3.2 A funco (pronuncia-se iota) representa uma enumerao desde 0 at um limite superior n exclusive, com os elementos da enumerao separados por um dado incremento, i.e.:
_$ (0 _$ (0 (iota 1 2 3 (iota 2 4 6 10 1) 4 5 6 7 8 9) 10 2) 8)

Dena a funo iota custa da funo enumera. Exerccio 1.3.3 Escreva uma funo membro? que recebe um objecto e uma lista e verica se aquele objecto existe na lista. Exerccio 1.3.4 Escreva uma funo elimina que recebe, como argumentos, um elemento e uma lista e devolve, como resultado, outra lista onde esse elemento no aparece. Exerccio 1.3.5 Escreva uma funo substitui que recebe dois elementos e uma lista como argumentos e devolve outra lista com todas as ocorrncias do segundo elemento substitudas pelo primeiro. Exerccio 1.3.6 Escreva uma funo remove-duplicados que recebe uma lista como argumento e devolve outra lista com todos os elementos da primeira mas sem duplicados, i.e.:
_$ (remove-duplicados (1 2 3 3 2 4 5 4 1)) (3 2 5 4 1)

1.4

Comparao de Smbolos e Listas

Vimos que o Lisp disponibiliza vrias funes de comparao e, em particular, a funo = aplicvel a nmeros, strings e smbolos. No entanto, quando aplicamos esta funo a listas os resultados no so os esperados:
_$ (= 1 1) T _$ (= "dois" "dois") T _$ (= (list 1 "dois") (list 1 "dois")) nil

Note-se que embora a funo no tenha dado qualquer erro (implicando que aplicvel a listas), devolveu falso a indicar que as listas so diferentes. Experimentemos agora uma variante:
_$ (setq lista-1-dois (list 1 "dois")) (1 "dois") _$ (= lista-1-dois lista-1-dois) T

Como se pode ver, a comparao foi feita entre duas referncia para uma lista criada anteriormente. Supreendentemente, o resultado agora armativo. Qual ento a semntica da funo = aplicado a listas? A resposta subtil mas muito importante: quando aplicada a dois argumentos do tipo lista, a funo = devolve verdade apenas quando os dois argumentos so a mesma lista.

1.5

Representao Grca de Listas

O que que quer dizer os dois argumentos so a mesma lista? Para conseguirmos responder a esta questo necessrio mergulharmos um pouco na organizao da memria do Lisp. Dissemos anteriormente que, ao fazermos (cons ), estamos a criar um par com os elementos e . Em termos da memria do Lisp, isto implica que ser reservada memria para conter esse par, que representamos gracamente com uma caixa dividida em duas metades, a mais esquerda a apontar para o primeiro argumento do cons (o car) e a mais direita a apontar para o segundo argumento do cons (o cdr). Por exemplo, a expresso (cons 1 "dois") pode-se representar nesta notao graca, denominada de caixa e ponteiro, tal como se segue:

"dois"

Obviamente, um cons pode apontar para outro cons. precisamente isso que acontece quando criamos listas. Como vimos, uma lista no mais do 10

que um cons cujo cdr aponta para outra lista (vazia ou no). Por exemplo, a lista criada pela expresso (list 1 2 3) absolutamente equivalente lista (cons 1 (cons 2 (cons 3 nil))). A sua representao grca : nil 1 2 3

1.6

Comparao de Listas

Quando atribuimos valores a variveis o que estamos a fazer, na realidade a estabelecer mais ponteiros, desta vez emanando das variveis e apontando para os seus valores. Consideremos, por exemplo, a seguinte interao:
_$ (1 _$ (1 _$ (1 (setq l1 (list 1 2 3)) 2 3) (setq l2 (list 1 2 3)) 2 3) (setq l3 l2) 2 3)

fcil de ver que a avaliao das expresses anteriores cria as seguintes estruturas: l1 1 l2 2 3 nil

l3 O aspecto crucial destas estruturas que l2 e l3 apontam para a mesma lista. Por este motivo, o operador = considera o valor de l2 igual ao valor de l3. J o mesmo no acontece quando se compara l2 com l1 pois as variveis apontam para diferentes estruturas. Este comportamento visvel na seguinte interaco:
_$ (= l1 l2) nil _$ (= l2 l3) T

Note-se que o que est em causa nas expresses anteriores apenas se os valores que so passados como argumentos da funo = so, na realidade, o mesmo valor. irrelevante se esses valores so provenientes da avaliao de variveis ou da avaliao de quaisquer outras expresses.

11

Apesar de termos usado o operador = em todas as comparaes que zemos, pragmaticamente falando, o seu uso costuma estar reservado para comparaes entre nmeros e strings, utilizando-se um outro operador diferente denominado eq para testar a igualdade de valores. A funo eq serve precisamente para testar se os seus dois argumentos so, na realidade, o mesmo objecto.1
Exerccio 1.6.1 Explique a seguinte interaco:
_$ (setq l1 (list 3 4 5)) (3 4 5) _$ (setq l2 (cons 1 (cons 2 l1))) (1 2 3 4 5) _$ (eq l1 l2) nil _$ (eq (cdr (cdr l2)) l1) T

1.7

Comparao de Smbolos

At agora, apenas temos usado smbolos para a construo dos nossos programas, como nomes de funes e variveis. No entanto, os smbolos so tambm teis noutros contextos em que so usados como valores. Por exemplo, como vimos na seco ??, a funo type devolve um smbolo que identica o tipo de objecto. Vimos tambm que, para a denio de um reconhecedor para um determinado tipo, era til poder fazer comparaes entre smbolos, de modo a identicarmos o smbolo pretendido:
(defun realp (obj) (= (type obj) real))

Acontece que, em Lisp, os smbolos gozam de uma propriedade muito importante: so nicos. Isto quer dizer que no existem dois smbolos diferentes com o mesmo nome. Assim, se duas expresses avaliarem para smbolos com o mesmo nome, ento essas expresses tm, como valor, exactamente o mesmo smbolo. Consequentemente, o operador mais adequado para fazer a comparao , mais uma vez, o eq. Por este motivo, a denio da funo realp deveria ser, na realidade:
(defun realp (obj) (eq (type obj) real))

1.8

Comparao Estrutural de Listas

At agora, a comparao de listas est restrita a sabermos se duas listas so, na realidade, a mesma lista. No entanto, frequentemente ocorre a necessidade de sabermos se duas listas so estruturalmente iguais. A igualdade estrutural dene-se de forma recursiva:
Em Auto Lisp, a funo eq consegue ainda comparar nmeros de tipos diferentes, como 1 e 1.0. Este comportamento, contudo, no usual na famlia de dialectos Lisp.
1

12

Qualquer objecto estruturalmente igual a si prprio. Dois cons so estruturalmente iguais se os cars forem estruturalmente iguais e os cdrs forem estruturalmente iguais. Esta denio sugere que temos de tratar distintamente os elementos atmicos dos no-atmicos. Se forem atmicos, ento s sero iguais se forem eq. Caso contrrio, se no so eq e algum deles atmico, no podem ser iguais. Finalmente, se nenhum deles atmico ento so ambos no-atmicos e se os cars forem iguais, os cdrs tambm tero de ser. Desta forma, podemos trivialmente denir a igualdade estrutural com uma funo igual cuja denio :
(defun igual? (obj1 obj2) (cond ((eq obj1 obj2) t) ((or (atom obj1) (atom obj2)) nil) ((igual? (car obj1) (car obj2)) (igual? (cdr obj1) (cdr obj2)))))

A linguagem Auto Lisp generaliza ainda esta funo de modo a que a comparao de entidades numricas possa contemplar uma determinada margem de erro: dois nmeros so considerados iguais se a sua diferena, em valor absoluto, inferior a esta margem de erro. A funo equal pr-denida em Auto Lisp recebe, para alm das entidades a comparar, um terceiro argumento opcional correspondente a esta margem de erro. Note-se o seguinte exemplo:
_$ (equal (list (list nil _$ (equal (list (list 0.05) T 1 3 (list 3 4)) 1.01 2.99 (list 3 4.01))) 1 3 (list 3 4)) 1.01 2.99 (list 3 4.01))

13

Carregamento de Ficheiros

medida que vamos denindo funes teis torna-se conveniente poder contar com elas sempre que necessrio. Para isso, o AutoCad disponibiliza uma funo que, a partir de um cheiro contendo todas as denies pretendidas, carrega essas denies para a sua memria. A funo denomina-se load2 e recebe, como argumento, a string com o caminho para o cheiro.3 Por exemplo, se o cheiro C:\AutoLisp\utilitarios.lsp contiver funes que pretendemos ter disponveis no AutoCad, a avaliao da expresso:
(load "C:\\AutoLisp\\utilitarios.lsp")

ir provocar a avaliao de todas as expresses contidas nesse cheiro e, no nal, devolve o valor da ltima expresso. Note-se, na expresso anterior, a utilizao de \\ em vez de \. Como vimos na secco ??, nas strings, o carcter \ um caracter de escape: serve para incluir na string outros caracteres como, por exemplo, as aspas. Infelizmente, o Windows usa o mesmo caracter como separador de directorias, pelo que somos obrigados a usar \\. Para simplicar, o Auto Lisp tambm admite o carcter / como separador de directorias em strings que designam caminhos para cheiros. Isto quer dizer que a expresso anterior tambm poderia ser escrita na forma:
(load "C:/AutoLisp/utilitarios.lsp")

Se, no caminho para o cheiro, se omitir a extenso deste,4 o Auto Lisp ir procurar pelo cheiro mais recente que tenha aquele nome e uma das extenses .vlx, .fas e .lsp. Mais frente veremos o signicado destas extenses. Infelizmente, o uso de caminhos completos para os nossos cheiros torna os nossos programas dependentes da localizao correcta desses cheiros. Isto diculta muito o trabalho de quem quer utilizar os nossos cheiros mas usa uma estrutura de directrios diferente da nossa. Para obviar a este problema, o Auto Lisp tem um mecanismo que permite evitar especicar o caminho para um cheiro, permitindo-nos escrever apenas:
(load "utilitarios.lsp")

ou, omitindo tambm a extenso:


(load "utilitarios")

Quando omitimos o caminho para o cheiro, ento o Auto Lisp ir procurar o cheiro segundo a seguinte estratgia:
O comando APPLOAD do AutoCad faz basicamente o mesmo mas usa uma interface grca para a seleco do cheiro. 3 A funo recebe ainda mais um argumento, opcional,... 4 A extenso de um cheiro o conjunto de (usualmente) trs letras que vem a seguir ao nome do cheiro e que separado deste por um ponto. A extenso usada normalmente para identicar o tipo do cheiro.
2

14

1. O cheiro procurado na directoria actual.5 2. Se no for encontrado, o cheiro procurado na directoria do desenho actual. 3. Se no for encontrado, o cheiro pesquisado em cada um dos directrios especicados no caminho de pesquisa do AutoCad. 4. Se no for encontrado, o cheiro pesquisado na directoria que contm o AutoCad. Uma vez que no conveniente alterar a directoria actual ou a directoria do desenho actual ou colocar os nossos cheiros misturados com os do AutoCad, a melhor soluo para facilmente podermos carregar os nossos utilitrios consiste em criar uma directoria onde colocamos os vrios cheiros e acrescentamos o caminho para essa directoria no caminho de pesquisa do AutoCad. Esse caminho especicado por uma sequncia de directrios indicados no tabulador Files (ou Ficheiros??? na verso Portuguesa do AutoCad), opo Support File Search Path (ou ???? na verso Portuguesa do AutoCad) do dilogo que resulta do comando OPTIONS. Note-se que a sequncia de directrios indicados nesse tabulador dene a ordem de pesquisa feita pelo AutoCad. Se pretendemos que os nossos cheiros sejam encontrados primeiro que outros com o mesmo nome, ento deveremos acrescentar o caminho para a nossa directoria antes dos outros caminhos na sequncia.

2.1

Carregamento de Dependncias

Em geral, cada cheiro de Lisp que criamos deve indicar explicitamente quais so as dependncias que tem de outros cheiros, i.e., quais so os cheiros que devem estar previamente carregados para que aquele possa ser tambm carregado. A ttulo de exemplo, consideremos um cheiro utils.lsp que dene operaes bsicas teis. Com base nestas operaes bsicas crimos vrias operaes sobre listas que guardmos num cheiro listas.lsp. Finalmente, temos um terceiro cheiro grafico.lsp que depende de algumas das operaes denidas em listas.lsp e ainda de algumas operaes destinadas a facilitar o desenho de grcos e que se encontram no cheiro grafico-utils.lsp. Para que possamos avaliar (load "grafico") e, com isso, carregar tudo o que preciso, conveniente que cada cheiro indique as suas dependncias. Assim, o contedo do cheiro grafico.lsp, dever ser algo da forma:
grafico.lsp (load "listas") (load "grafico-utils") (defun ...) ...
5 Esta a directoria em que o AutoCad comea a funcionar. No caso do Windows, corresponde ao valor da opo Start In das propriedades do cone do AutoCad.

15

utils

listas

grafico-utils

grafico Figura 1: Dependncias entre os cheiros grafico, listas, utils e grafico-utils. Naturalmente, o cheiro listas.lsp ser da forma:
listas.lsp (load "utils") (defun ...) ...

Deste modo, quando avaliamos (load "grafico") o Auto Lisp localiza o cheiro em questo e, uma vez que a primeira expresso desse cheiro (load "listas"), o Auto Lisp suspende momentaneamente o carregamento do cheiro grafico e procura pelo cheiro listas. Quando o encontra, comea a avaliar as suas expresses mas, mais uma vez, como a primeira dessas expresses (load "utils") o Auto Lisp suspende momentaneamente o carregamento do cheiro e procura pelo cheiro utils. Quando o encontra, avalia todo o seu contedo, regressando ento ao carregamento do cheiro listas. Aps ter avaliado todas as expresses deste cheiro regressa nalmente ao primeiro cheiro grafico para passar avaliao da expresso seguinte: (load "grafico-utils"). Novamente, o Auto Lisp suspende momentaneamente o carregamento do cheiro grafico, para agora procurar pelo cheiro grafico-utils, avaliar todas as suas expresses e, por m, regressar ao cheiro grafico para avaliar as restantes formas. Esta relao de dependncia entre os vrios cheiros pode ser representada gracamente num grafo, tal como se apresenta na Figura 1.

2.2

Mdulos

A um conjunto de cheiros que, juntos, providenciam uma determinada funcionalidade, chamamos um modulo. Assim, os cheiros grafico, listas, utils e grafico-utils constituem um mdulo. O mesmo se poder dizer do subconjunto constitudo pelos cheiros listas e utils e, no limite, do subconjunto constitudo pelo cheiro utils. Assim, seria mais correcto dizer que existe um mdulo de utilitrios constitudo pelo nico cheiro utils, existe um mdulo de listas que depende do mdulo de utilitrios e que composto pelo cheiro listas e, nalmente, existe um mdulo de grcos que depende do mdulo de listas e que constitudo pelos cheiros grafico e grafico-utils. 16

O conceito de mdulo muito importante pois permite abstrair um conjunto de cheiros numa s entidade. Do ponto de vista de quem usa o mdulo, irrelevante saber se ele constitudo por um, dois ou mil cheiros, qual a ordem de carregamento desses cheiros ou quais as dependncias que esse mdulo tem de outros mdulos. Desde que no se reduza a funcionalidade de um mdulo, esta abstraco permite que quem criou um mdulo possa alter-lo, acrescentando, retirando ou modicando cheiros, sem afectar quem depende do mdulo. Consideremos agora uma situao mais complexa (mas mais frequente). Para alm dos cheiros grafico-utils.lsp, grafico.lsp, listas.lsp e utils.lsp que tnhamos no exemplo anterior, temos ainda, num outro cheiro chamado testes.lsp, operaes teis para a realizao de testes, operaes essas cuja denio depende, por um lado, de algumas das operaes de utils e, por outro, de algumas das operaes de listas, tal como se apresenta em seguida:
testes.lsp (load "utils") (load "listas") (defun ...) ...

Finalmente, temos um cheiro colunas.lsp contendo operaes para o desenho de colunas e que depende, quer de testes, quer de utils, quer de graficos:
colunas.lsp (load "testes") (load "utils") (load "graficos") (defun ...) ...

Estas relaes de dependncias esto representadas no grafo presente na Figura 2. Se colunas depende de testes e testes depende de utils ento redundante dizer que colunas depende de utils. De facto, a relao de dependncia uma relao transitiva, i.e., se x depende de y e y depende de z , ento automaticamente x depende de z . Isto sugere que, no cheiro colunas, deveramos remover a expresso que carrega o cheiro utils pois ele j ter sido ser carregado automaticamente em consequncia da avaliao da expresso anterior (load "testes"). Contudo, existe um pressuposto importante neste raciocnio: para que colunas.lsp possa dispensar o carregamento de utils, preciso que o autor de colunas.lsp saiba que utils uma dependncia de testes. Ora isso implica que temos de conhecer a estrutura interna do mdulo de testes, o que viola a abstraco desse mdulo. Essa violao tem como consequncia 17

utils listas grafico-utils

testes

grafico

colunas Figura 2: Dependncias entre os cheiros grafico, listas, utils e grafico-utils. que quaisquer alteraes que o autor do mdulo de testes zer na estrutura do seu mdulo poder ter implicaes negativas para o mdulo de colunas. Por exemplo, se o autor do mdulo de testes decidir terminar a dependncia que tinha de utils, ento o mdulo de colunas ir ter problemas pois o cheiro utils, anal, j no carregado. Por este motivo, sempre prefervel que cada mdulo indique explicitamente as suas dependncias de outros mdulos e no assuma qualquer conhecimento sobre a estrutura interna ou dependncias desses mdulos. Infelizmente, preservar a abstraco dos mdulos acarreta um problema: um cheiro pode acabar por ser carregado mltiplas vezes. Para percebermos isto, pensemos na sequncia de carregamentos que ocorre quando avaliamos (load "colunas"): 1. (load "testes") (a) (load "utils") (b) (load "listas") i. (load "utils") 2. (load "utils") 3. (load "grafico") (a) (load "listas") i. (load "utils") 4. (load "grafico-utils") fcil ver que, ao preservarmos a abstraco de cada mdulo, i.e., ao no conhecermos as dependncias de cada mdulo, acabamos inadvertidamente por carregar mltiplas vezes vrios cheiros: listas carregado duas vezes e utils carregado quatro vezes. Obviamente, este mltiplo carregamento de cheiros indesejvel. Para resolver este problema, devemos complementar o processo de carregamento 18

com um registo de tudo o que j tiver sido carregado para evitar carregar algum cheiro mais do que uma vez. Para isso, vamos comear por usar uma varivel global para conter os nomes dos cheiros j carregados e, em seguida, denimos uma funo usa que, dado o nome de um cheiro que se pretende usar, verica se ele j foi carregado e, se no tiver sido, carrega-o. Estas duas formas sero colocadas no cheiro usa.lisp:
usa.lsp (setq ficheiros-carregados ()) (defun usa (nome) (if (member nome ficheiros-carregados) nil (progn (load nome) (setq ficheiros-carregados (cons nome ficheiros-carregados)) t)))

Usando a funo usa no lugar de load, passamos a ter os cheiros:


grafico.lsp (usa "listas") (usa "grafico-utils") (defun ...) ... listas.lsp (usa "utils") (defun ...) ... testes.lsp (usa "utils") (usa "listas") (defun ...) ... colunas.lsp (usa "testes") (usa "utils") (usa "graficos") (defun ...) ...

Agora, ao avaliarmos a expresso (load "colunas") obtemos o seguinte comportamento: 1. (usa "testes") Ainda no foi carregado. 19

2. (load "testes") (a) (b) (c) (d) (usa "utils") Ainda no foi carregado. (load "utils") (usa "listas") Ainda no foi carregado. (load "listas") i. (usa "utils") J foi carregado.

3. (usa "utils") J foi carregado. 4. (usa "grafico") Ainda no foi carregado. (a) (usa "listas") J foi carregado. 5. (usa "grafico-utils") Ainda no foi carregado. Como podemos ver, agora cada cheiro carregado uma nica vez.

2.3

Carregamento Automtico

Com a funo uso j temos a possibilidade de carregar os mdulos de que necessitamos sem nos preocuparmos com carregamentos duplicados. Infelizmente, esta funo s est disponvel depois de a carregarmos, o que inconveniente pois temos de nos lembrar de carregar manualmente o cheiro "usa"sempre que iniciamos o trabalho em AutoCad. Para obviar esta inconvenincia, o AutoCad permite o carregamento automtico de cheiros segundo o seguinte esquema de cheiros: O cheiro acad.lsp carregado automaticamente quando o AutoCad inicia. O cheiro acaddoc.lsp carregado automaticamente quando abrimos um desenho. O AutoCad pesquisa por estes cheiros usando as mesmas regras anunciadas anteriormente para localizar cheiros sem a indicao do caminho. Este esquema permite-nos ter um cheiro de inicializao que carrega tudo aquilo que entendemos ser til para a nossa sesso de trabalho. Se for til independentemente do desenho em questo, dever ser denido ou carregado no cheiro acad.lsp. Se for til para um s desenho, ento o melhor ter um cheiro acaddoc.lsp contendo tudo o que for preciso e guardado na directoria desse desenho. Desta forma, diferentes desenhos (guardados em diferentes directorias) podem ter diferentes cheiros de inicializao acaddoc.lsp. H ainda outros cheiros que o AutoCad carrega automaticamente mas que esto relacionados com outros recursos do AutoCad e no com o Auto Lisp. Tendo esta informao em conta, deveremos criar um cheiro acad.lsp numa das directorias presentes no caminho de pesquisa do AutoCad e, nesse cheiro, devermos incluir a seguinte expresso:
acad.lsp (load "usa")

20

2.4

Interpretao e Compilao

Vimos que, quando o Auto Lisp avalia uma expresso, ele primeiro analisaa, decompondo-a nas suas subexpresses e, recursivamente, avalia estas de acordo com as regras da linguagem, seguindo este processo at chegar s expresses mais simples de todas, para as quais o Auto Lisp sabe imediatamente qual o valor, passando ento a combinar os valores das subavaliaes at produzir o valor da expresso original. Este processo, que o Auto Lisp segue para a avaliao de qualquer expresso, denomina-se interpretao e o programa que o realiza diz-se um interpretador. Quando estamos a usar o REPL do AutoCad estamos, na realidade, a usar um interpretador para a linguagem Auto Lisp. Embora a interpretao seja um processo muito til para a depurao dos programas, sofre de um problema: computacionalmente pouco eciente. Se pensarmos, por exemplo, na funo factorial, fcil vermos que a avaliao de uma invocao da funo factorial implica uma sequncia de anlises e combinao de resultados que tem de ser repetida para cada uma das sucessivas invocaes recursivas da funo. Existe um outro processo de avaliao que, embora menos exvel que a interpretao, tem o potencial para ser muito mais eciente: a compilao. A compilao uma operao que realizada por um compilador e que se baseia em analizar expresses sem as avaliar e em transform-las noutras expresses equivalentes que, quando avaliadas, produzam os mesmos resultados mas de forma mais eciente. A ideia fundamental da compilao que possvel converter um programa escrito numa linguagem num programa escrito noutra linguagem que seja mais eciente de interpretar ou, no limite, que seja directamente executvel pelo computador. Em geral, a compilao em Lisp transforma um cheiro contendo expresses Lisp num outro cheiro com as mesmas expresses traduzidas para outra linguagem muito mais eciente de avaliar. Em geral, h duas vantagens em usar cheiros compilados: muito mais rpido carregar um cheiro compilado do que um no compilado. A invocao das funes que foram denidas a partir de um cheiro compilado mais rpida, por vezes, muito mais rpida. Para se compilar um cheiro necessrio invocar a funo vlisp-compile. Esta funo recebe trs argumentos: 1. Um smbolo indicando qual o grau de optimizao desejado. 2. Uma string indicando o cheiro a compilar. Se a extenso do cheiro for omissa, considera-se .lsp como extenso. Se apenas se indicar o nome do cheiro, sem o caminho at ele, o Visual Lisp usa o processo normal de procura de cheiros para o encontrar.

21

3. Uma string opcional indicando o caminho para o cheiro a criar para conter o resultado da compilao. Quando omisso, idntico ao argumento anterior, mas usando .fas como extenso. Quando no omisso, ento poder ser apenas o nome do cheiro (caso em que o o Visual Lisp ir colocar o cheiro na sua directoria de instalao) ou o caminho completo para o cheiro. Em relao ao nvel de optimizao, h trs possibilidades (indicadas pelos smbolos st, lsm e lsa) mas apenas a primeira preserva (quase) todas as caractersticas da linguagem Lisp. As seguintes alteram ligeiramente a semntica da linguagem para conseguirem obter maior performance. Por exemplo, para l do nvel st, a redenio de funes deixa de ter efeito e a informao de depurao cada vez menor, tornando os programas mais ecientes mas dicultando a vida ao programador quando ele tenta perceber qualquer erro que tenha ocorrido.6 Como exemplo de utilizao, a seguinte expresso mostra a compilao do cheiro listas.lsp:7
(vlisp-compile st "listas")

6 Essa diculdade de compreenso pode ser til quando se pretende ceder a terceiros um programa mas em que no se pretende que se saiba como est feito. 7 Note-se a utilizao da plica para evitar a avaliao do smbolo st.

22

Listas de Coordenadas

As listas constituem um tipo de dados extraordinariamente til. Atravs da utilizao de listas possvel escrever programas mais simples, mais claros e mais genricos. Vimos anteriormente que sempre que pretendiamos indicar no AutoCad uma posio geomtrica no plano bidimensional usvamos os construtores de coordenadas Cartesianas ou polares que, por sua vez, criavam lista com as coordenadas correspondentes, contendo dois elementos no caso bi-dimensional e trs elementos no caso tri-dimensional. Uma outra utilizao frequente de listas ocorre quando se pretende agrupar um conjunto de entidades geomtricas. Neste caso, contrariamente ao que acontecia com a utilizao de listas para representar posies 2D ou 3D, a lista usualmente no possui um nmero xo de elementos.

3.1

Polgonos

Imaginemos, a ttulo de exemplo, que pretendemos representar um polgono. Por denio, um polgono uma gura plana limitada por um caminho fechado composto por uma sequncia de segmentos de recta. Cada segmento de recta uma aresta (ou lado) do polgono. Cada ponto onde se encontram dois segmentos de recta um vrtice do polgono. A partir da denio de poligono fcil vermos que uma das formas mais simples de representar um polgono ser atravs de uma sequncia de vrtices que indica qual a ordem pela qual devemos unir os vrtices com uma aresta e onde se admite que o ltimo elemento ser unido ao primeiro. Para representar esta sequncia podemos simplesmente usar uma lista. Por exemplo, a lista ((0 0) (1 0) (1 1) (0 1)) representa um quadrado (i.e., um quadriltero equiltero equiangular). A lista ((0 0) (1 0) (1 1)) representa um tringulo (mais especicamente, um tringulo rectngulo issceles). Para um exemplo mais complexo, considere-se o octgono (irregular) representado pela lista ((0 1) (1 0) (2 0) (3 1) (3 2) (2 3) (1 3) (0 2)). Reparemos que qualquer uma das trs listas anteriores contm sublistas: as coordenadas dos pontos. Isso, no entanto, um facto irrelevante para a lista em si pois uma lista pode conter elementos de qualquer tipo, incluindo, obviamente, outras listas.
Exerccio 3.1.1 Escreva trs expresses Lisp que, quando avaliadas, construam a representao dos trs polgonos descritos acima.

Como se pode deduzir dos exemplos anteriores, o nmero de elementos da lista que representa um polgono varivel: um tringulo representado por uma lista de trs pontos, um quadriltero representado por uma lista de quatro pontos, um octgono representado por uma lista de oito pontos, um hentriacontgono representado por uma lista de trinta e um pontos, etc. A representao de polgonos usando listas apenas o primeiro passo para os podermos visualizar gracamente. O segundo passo a invocao dos comandos do AutoCad que constroem o polgono em questo. Para isso, temos 23

Figura 3: Polgonos em AutoCad. de invocar o comando line (ou, alternativamente, o comando pline) e, de seguida, passar os sucessivos pontos para o AutoCad. No nal da sequencia de pontos temos ainda de passar a opo close para que o AutoCad feche o polgono, unindo o ltimo ao primeiro ponto. Decompondo o problema em duas partes, podemos escrever:
(defun poligono (vertices) (command "_.line") (passa-pontos vertices) (command "_close"))

Uma vez que a passagem dos pontos tem de ser feita percorrendo a lista com os vrtices, vamos denir uma funo recursiva para o fazer:
(defun passa-pontos (pontos) (if (null pontos) nil (progn (command (car pontos)) (passa-pontos (cdr pontos)))))

Usando a funo poligono podemos agora facilmente desenhar inmeros polgonos. Por exemplo, as seguintes avaliaes produzem o resultado apresentado na Figura 3.
(poligono (list (xy -2 -1) (xy 0 2) (xy 2 -1))) (poligono (list (xy -2 1) (xy 0 -2) (xy 2 1)))

importante notarmos que a funo poligono independente de quaisquer polgonos em particular. Ela tanto se aplica ao desenho de tringulos como quadrados como qualquer outro polgono. Esta independncia tornouse possvel pela utilizao de listas para representar as coordenadas dos vrtices dos polgonos. Agora, apenas necessrio concentrarmo-nos na criao destas listas de coordenadas. 24

Figura 4: Variaes de Estrelas numa janela do Forte de Amber, localizado no estado de Jaipur, na ndia. Fotograa de David Emmett Cooley. 3.1.1 Estrelas Regulares

O polgono representado na Figura 3 foi gerado manualmente pela sobreposio de dois tringulos. Um polgono mais interessante o famoso pentagrama, ou estrela de cinco pontas, que tem sido usado com conotaes simblicas, mgicas ou decorativas desde os tempos da Babilnia.8 A Figura 4 demonstra o uso do pentagrama (bem como do octograma, do octgono e do hexgono) como elemento decorativo e estrutural numa janela de pedra. parte as conotaes extra-geomtricas, o pentagrama , antes de tudo, um polgono. Por este motivo, desenhvel pela funo poligono desde que consigamos produzir uma lista com as coordenadas dos vrtices. Para isso, vamos reportar-nos Figura 5 onde visvel que os cinco vrtices do pentagrama dividem o crculo em 5 partes, com arcos de 25 cada. Dado o centro do pentagrama, o seu vrtice superior faz um ngulo de 2 com o eixo das abcissas. Esse vrtice deve ser unido, no com o vrtice seguinte, mas sim com o imediata 2 mente a seguir a esse, i.e., aps uma rotaco de dois arcos ou 25 = 45 = 0.8 . Estamos agora em condies de denir a funo vertices-pentagrama que constri a lista com os vrtices do pentagrama:
(defun vertices-pentagrama (centro (list (+pol centro raio (+ (/ pi (+pol centro raio (+ (/ pi (+pol centro raio (+ (/ pi
8

raio) 2) (* 0 0.8 pi))) 2) (* 1 0.8 pi))) 2) (* 2 0.8 pi)))

O pentagrama foi smbolo de perfeio matemtica para os Pitagricos, foi smbolo do domnio do esprito sobre os quatro elementos da matria pelos ocultistas, representou as cinco chagas de Cristo crucicado para os Cristos, foi associado s propores do corpo humano, foi usado como smbolo manico e, quando invertido, at foi associado ao satanismo.

25

2 5

Figura 5: Construo do Pentagrama.


(+pol centro raio (+ (/ pi 2) (* 3 0.8 pi))) (+pol centro raio (+ (/ pi 2) (* 4 0.8 pi))))) (poligono (vertices-pentagrama (xy 0 0) 1))

Como bvio, a funo vertices-pentagrama possui excessiva repetio de cdigo, pelo que seria prefervel encontrarmos uma forma mais estruturada de gerarmos aqueles vrtices. Para isso, vamos comear por pensar na generalizao da funo. O caso geral de um pentagrama a estrela regular, em que se faz variar o nmero de vrtices e o nmero de arcos que separam um vrtice do vrtice a que ele se une. O pentagrama um caso particular da estrela regular em que o nmero de vrtices cinco e o nmero de arcos de separao dois. Matematicamente falando, uma estrela regular representa-se pelo smbolo de v Schli9 { a } em que v o nmero de vrtices e a o nmero de arcos. Nesta notao, um pentagrama escreve-se como { 5 2 }. Para desenharmos estrelas regulares vamos idealizar uma funo que, dado o centro da estrela, o raio do crculo circunscrito, o nmero de vrtices v (a que chamaremos n-vertices) e o nmero de arcos de separao a (a que chamaremos n-arcos), calcula o tamanho do arco que preciso avanar a partir de cada vrtice. Esse arco , obviamente, = a 2v . Tal como no pentagrama, para primeiro vrtice vamos considerar um ngulo inicial de = 2. A partir desse primeiro vrtice no preciso mais do que ir aumentando o ngulo de de cada vez. As seguintes funes implementam este raciocnio:
(defun vertices-estrela (p raio n-vertices n-arcos) (lista-vertices p raio n-vertices pi/2 (* n-arcos (/ 2*pi n-vertices)))) (defun lista-vertices (p raio n fi d-fi)
Ludwig Schli foi um matemtico e gemetra Susso que fez importantes contribuies, em particular, na geometria multidimensional.
9

26

Figura 6: Estrelas regulares em AutoCad. Da esquerda para a direita, temos 7 7 8 um pentagrama ({ 5 2 }), dois heptagramas ({ 2 } e { 3 }) e um octograma ({ 3 }).
(if (= n 0) (list) (cons (+pol p raio fi) (lista-vertices p raio (- n 1) (+ fi d-fi) d-fi))))

Com a funo vertices-estrela agora trivial gerar os vrtices de qualquer estrela regular. A Figura 6 apresenta as estrelas regulares desenhadas a partir das seguintes expresses:
(poligono (vertices-estrela (xy 0 0) 1 5 2)) (poligono (vertices-estrela (xy 2 0) 1 7 2)) (poligono (vertices-estrela (xy 4 0) 1 7 3)) (poligono (vertices-estrela (xy 6 0) 1 8 3))

muito importante salientarmos a forma como a representao grca de estrelas est separada em duas partes. De um lado, o da funo vertices-estrela, produzimos as coordenadas dos vrtices das estrelas, pela ordem em que estes devem ser unidos. No outro lado, o da funo poligono, usamos essas coordenadas para criar uma representao grca dessa estrela baseada em linhas que ligam os seus vrtices. A passagem das coordenadas de um lado para o outro realizada atravs de uma lista que produzida num lado e consumida no outro. Esta utilizao de listas para separar diferentes processos fundamental e ser por ns repetidamente explorada para simplicar os nossos programas. 27

Figura 7: Polgonos regulares. Da esquerda para a direita temos um tringulo equiltero, um quadrado, um pentgono regular, um hexgono regular, um heptgono regular, um octgono regular, um enegono regular e um decgono regular. 3.1.2 Polgonos Regulares

Um polgono regular um polgono que tem todos os lados de igual comprimento e todos os ngulos de igual amplitude. A Figura 7 ilustra exemplos de polgonos regulares. Como bvio, um polgono regular um caso particular de uma estrela regular em que o nmero de arcos de separao um. Para criarmos polgonos regulares, vamos denir uma funo denominada vertices-poligono-regular que gera uma lista de coordenadas correspondente s posies dos vrtices de um polgono regular de n lados, inscrito numa circunferncia de raio r centrada em p, cujo primeiro vrtice faz um ngulo com o eixo dos X . Sendo um polgono regular um caso particular de uma estrela regular, basta-nos invocar a funo que computa os vrtices de uma estrela regular mas usando, para o parmetro , apenas a diviso da circunferncia 2 pelo nmero de lados n, i.e.:
(defun vertices-poligono-regular (p r fi n) (lista-vertices p r n fi (/ 2*pi n)))

3.2

Iterao em Listas

At agora temos processado as listas usando apenas recurso. Sendo a lista um tipo de dados recursivo natural que a forma mais simples de computar valores a partir de listas seja atravs de funes recursivas. No entanto, existem casos, como o da funo passa-pontos, em que o nico resultado relevante da recurso o conjunto de efeitos secundrios que vo sendo realizados medida que a recurso se vai desenrolando. De facto,

28

se observarmos com ateno a denio da referida funo constatamos que ocorre a seguinte reduo: (passa-pontos l) (passa-pontos (cdr l)) Contrariamente ao que acontecia nos casos tradicionais de recurso, neste nada computado com o resultado da invocao recursiva, ou seja, a invocao recursiva nada deixa pendente. Esta forma de recurso denomina-se iterao. Para estes casos em que apenas interessam os efeitos secundrios do processamento dos vrios elementos de uma lista o Auto Lisp providencia uma forma que simplica substancialmente os programas: a forma foreach. A sua sintaxe a seguinte:
(foreach nome expresso expresso1 . . . expresson )

A forma foreach recebe o nome de uma varivel, uma expresso que deve avaliar para uma lista e vrias expresses que sero avaliadas para cada elemento da lista, com o nome associado a esse elemento. Por exemplo, a seguinte expresso escreve todos os elementos de uma lista:
(foreach numero (list 1 2 3 4 5) (print numero))

Usando a forma foreach agora mais fcil denir a funo passa-pontos:


(defun passa-pontos (pontos) (foreach ponto pontos (command ponto)))

Na realidade, a funo cou to simples que prefervel elimin-la e juntar os vrios comandos na funo poligono:
(defun poligono (vertices) (command "_.line") (foreach vertice vertices (command vertice)) (command "_close"))

A iterao em listas tambm muito til para experimentarmos variaes de guras geomtricas. Uma vez que a funo enumera (denida na seco 1.3) gera uma sequncia de nmeros, podemos usar esses nmeros em combinao com a forma foreach para desenhar uma sequncia de guras. Por exemplo, a seguinte expresso gera a Figura 8:
(foreach a (enumera 1 9 1) (poligono (vertices-estrela (xy (* 2 a) 0) 1 20 a)))

29

Figura 8: Estrelas regulares { p r } com p = 20 e r a variar desde 1 ( esquerda) at 9 ( direita).

3.3

Linhas Poligonais e Splines

Vimos que as listas permitem armazenar um nmero varivel de elementos e vimos como possvel usar listas para separar os programas que denem as coordenadas das guras geomtricas daqueles que usam essas coordenadas para as representarem gracamente. No caso da funo poligono, as listas de coordenadas so usadas para a criao de polgonos, i.e., guras planas limitadas por um caminho fechado composto por uma sequncia de segmentos de recta. Acontece que nem sempre queremos que as nossas guras sejam limitas por caminhos fechados, ou que esses caminhos sejam uma sequncia de segmentos de recta ou, sequer, que as guras sejam planas. Para resolver este problema, necessitamos de denir funes capazes de, a partir de listas de coordenadas, criarem outros tipos de guras geomtricas. Assim, vamos deixar de falar em vrtices e vamos passar a falar genericamente em coordenadas dos pontos que denem as guras geomtricas. O caso mais simples o de uma linha poligonal (potencialmente aberta). J vimos que o comando line permite criar linhas poligonais compostas por segmentos de recta independentes. Outra alternativa ser no ter segmentos independentes mas sim uma nica entidade geomtrica. Para isso, podemos usar o comando pline:
(defun pline-pontos (pontos) (command "_.pline") (foreach p pontos (command p)) (command ""))

Note-se que no usmos a opo "close" para no fecharmos a linha poligonal. Convm salientar que o comando pline, embora seja mais eciente que o comando line, no consegue criar linhas poligonais que no sejam paralelas ao plano XY . No caso geral em que pretendemos linhas poligonais tridimensionais, este comando no pode ser usado e teremos de passar para uma variante menos eciente mas mais verstil: o comando 3dpoly. Para isso, vamos denir a funo 3dpoly-pontos de modo a usar linhas poligonais tridimensionais:
(defun 3dpoly-pontos (pontos) (command "_.3dpoly")

30

Figura 9: Comparao entre uma linha poligonal e uma spline que unem o mesmo conjunto de pontos.
(foreach p pontos (command p)) (command ""))

No caso de no pretendermos linhas poligonais mas sim curvas mais suaves podemos empregar uma spline que passe pelos pontos dados, atravs do comando spline do AutoCad:
(defun spline-pontos (pontos) (command "_.spline") (foreach p pontos (command p)) (command "" "" ""))

A diferena entre uma linha poligonal (produzida por line, pline, ou 3dpoly) e uma spline visvel na Figura 9, onde comparamos uma sequncia de pontos unida com uma linha poligonal com a mesma sequncia unida com uma spline. O grco foi produzido pela avaliao das seguintes expresses:
(setq pontos (list (xy 0 2) (xy 1 4) (xy 2 0) (xy 3 3) (xy 5 0) (xy 5 4) (xy 6 0) (xy 7 2) (xy 8 1) (xy 9 4))) (pline-pontos pontos) (spline-pontos pontos)

Naturalmente, a especicao manual das coordenadas dos pontos pouco conveniente, sendo prefervel que essas coordenadas sejam computadas automaticamente de acordo com uma especicao matemtica da curva pretendida. Imaginemos, por exemplo, que pretendemos traar uma curva sinuside a partir de um ponto P . Infelizmente, de entre as guras geomtricas disponibilizadas pelo AutoCadpontos, linhas rectas, rectngulos, polgonos, 31

crculos, arcos de crculo, elipses, arcos de elipse, donuts, linhas poligonais e splinesno consta a sinuside. A alternativa criarmos uma aproximao a uma sinuside. Para isso, podemos calcular uma sequncia de pontos pertencentes curva da sinuside e traar rectas ou, ainda melhor, curvas que passem por esses pontos. Para calcular a sequncia de pontos da sinuside, podemos considerar uma sequncia de valores da coordenada x e, para cada um, calculamos a coordenada y atravs da aplicao da funo seno coordenada x. Com os valores de x e y , criamos as coordenadas a partir do ponto P . Este processo implementado pela funo pontos-seno:
(defun pontos-seno (p xs) (if (null xs) () (cons (+xy p (car xs) (sin (car xs))) (pontos-seno p (cdr xs)))))

A Figura 10 mostra as curvas traadas pelas seguintes expresses que unem os pontos por intermdio de plines:
(pline-pontos (pontos-seno (xy 0 1) (list 0.0 1.0 2.0 3.0 4.0 5.0 6.0))) (pline-pontos (pontos-seno (xy 0 0.5) (list 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5))) (pline-pontos (pontos-seno (xy 0 0) (list 0.0 0.2 2.0 2.2 4.0 4.2 6.0 6.2

0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.4 2.6 2.8 3.0 3.2 3.4 3.6 3.8 4.4 4.6 4.8 5.0 5.2 5.4 5.6 5.8 6.4)))

Note-se, na Figura 10, que demos um deslocamento vertical s curvas para melhor se perceber a diferena de preciso entre elas. Como plenamente evidente, quantos mais pontos se usarem para calcular a curva, mais proxima ser a linha poligonal da verdadeira curva. No entanto h tambm que ter em conta que o aumento do nmero de pontos obriga o AutoCad a maior esforo computacional. Para se obterem ainda melhores aproximaes, embora custa de ainda maior esforo computacional, podemos usar splines, simplesmente mudando as expresses anteriores para usarem a funo spline-pontos no lugar de pline-pontos. O resultado est visvel na Figura 11. 32

Figura 10: Senos desenhados usando plines com um nmero crescente de pontos.

Figura 11: Senos desenhados usando splines com um nmero crescente de pontos.

33

Apesar de j termos os meios para a criao de curvas muito prximas do ideal matemtico, o processo de especicao dos pontos ainda pouco prtico pois, tal como se viu nos exemplos anteriores, obriga-nos a criar manualmente as listas com os valores das abcissas. Para resolver esse problema existem duas solues. A mais directa consiste em empregar a funo enumera que denimos na seco 1.3 para gerar os valores das abcissas. Usando esta funo, as curvas anteriores poderiam ter sido geradas pelas expresses:
(spline-pontos (pontos-seno (xy 0 1) (enumera 0.0 6.0 1.0))) (spline-pontos (pontos-seno (xy 0 0.5) (enumera 0.0 6.5 0.5))) (spline-pontos (pontos-seno (xy 0 0) (enumera 0.0 6.4 0.2)))

Uma outra soluo consiste em incorporar na funo pontos-seno o prprio processo de gerao dos valores das abcissas. Para isso, podemos redenir a funo pontos-seno de forma a receber, no lugar na lista de abcissas, os parmetros do intervalo de valores a gerar, i.e., os limites inferior x0 e superior x1 e o incremento x a considerar. Para calcular o conjunto de valores para a funo seno no intervalo [x0 , x1 ] basta-nos comear no ponto x0 e passar do ponto xi para o ponto xi+1 atravs de xi+1 = xi + x e ir sucessivamente calculando o valor da expresso sin(xi ) at que xi exceda x1 . Para obtermos uma denio recursiva para este problema podemos pensar que quando x0 > x1 o resultado ser uma lista vazia de coordenadas, caso contrrio juntamos a coordenada p + (x0 , sin(x0 )) lista de coordenadas do seno para o intervalo [x0 + dx, x1 ]. Esta denio traduzida directamente para a seguinte funo:
(defun pontos-seno (p x0 x1 dx) (if (> x0 x1) () (cons (+xy p x0 (sin x0)) (pontos-seno p (+ x0 dx) x1 dx))))

Usando esta nova denio da funo pontos-seno, as curvas representadas na Figura 11 poderiam ter sido geradas por expresses ainda mais simples:
(spline-pontos (pontos-seno (xy 0 1) 0.0 6.0 1.0)) (spline-pontos

34

(pontos-seno (xy 0 0.5) 0.0 6.5 0.5)) (spline-pontos (pontos-seno (xy 0 0) 0.0 6.4 0.2)) Exerccio 3.3.1 A nova verso da funo pontos-seno permite gerar curvas sinusides com mais ecincia que a antiga. Porqu?

35

Manipulao de Entidades

O AutoCad no s um programa de desenho. Ele tambm uma base de dados sobre guras geomtricas. De facto, sempre que desenhamos algo o AutoCad regista na sua base de dados a entidade grca criada, bem como um conjunto de informao relacionado com essa entidade. Existem vrias maneiras de se aceder s entidades criadas. Uma das mais simples para um utilizador normal do AutoCad ser atravs do uso do rato, simplesmente clicando na entidade grca a que pretendemos aceder. Outra, mais til, para quem pretende programar em AutoCad, ser atravs da invocao de funes do Auto Lisp que devolvem, como resultados, as entidades geomtricas criadas. H vrias destas funes nossa disposio mas, por agora, vamos limitar-nos a uma das mais simples: a funo entlast. A funo entlast no recebe quaisquer argumentos e devolve a ltima entidade criada no AutoCad. Para alm de registar todas as entidades geomtricas criadas durante uma sesso de desenho, o AutoCad mantm ainda um registo (que vai sendo continuamente actualizado) da ltima dessas entidades, de modo a que o programador a ela possa aceder simplesmente invocando a funo entlast. Se ainda no tiver sido criada nenhuma entidade, a funo devolve nil. A seguinte interaco demonstra o comportamento desta funo aps a criao de um crculo:
_$ (command "_.circle" (xy 10 20) 30) nil _$ (entlast) <Entity name: 7ef91ed8>

O valor retornado pela avaliao de (entlast) tem, como representao externa, o texto <Entity name: ???>, em que ??? um identicador da entidade, destinado apenas a permitir ao leitor distinguir diferentes entidades entre si pois cada entidade ter necessariamente um identicador diferente. Dada uma entidade, podemos estar interessados em conhecer as suas propriedades. Para isso, o Auto Lisp disponibiliza a funo entget. Esta funo recebe uma entidade como argumento e devolve uma lista com as propriedades da entidade. Eis um exemplo:10
_$ (entget (entlast)) ((-1 . <Entity name: 7ef91ed8>) (0 . "CIRCLE") (330 . <Entity name: 7ef91db8>) (5 . "5B") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "0") (100 . "AcDbCircle") (10 10.0 20.0 0.0)
10

Indentmos o resultado da avaliao para melhor se compreender o valor devolvido.

36

(40 . 30.0) (210 0.0 0.0 1.0))

Como podemos ver, o valor devolvido uma lista de pares, excepto para os ltimo e o antepenltimo elementos da lista, i.e., (210 0.0 0.0 1.0) e (10 10.0 20.0 0.0) que no aparentam ter a forma de pares mas isso acontece simplesmente porque o segundo elemento de cada um desses pares , ele prprio uma lista. Como j vimos aquando da relao entre pares e listas, aqueles elementos, na realidade, correspondem, respectivamente, aos pares (210 . (0.0 0.0 1.0)) e (10 . (10.0 20.0 0.0)).

4.1

Listas de Associaes

Este tipo de lista cujos elementos so pares denomina-se uma lista de associaes ou, abreviadamente, uma assoc-list ou, ainda mais abreviadamente, uma alist. No resultado devolvido pela ltima expresso h alguns elementos facilmente relacionveis com o crculo que tnhamos criado, nomeadamente, a string "CIRCLE", as coordenadas (10.0 20.0 0.0) e o raio 30. Menos claro o signicado dos nmeros que aparecem como primeiro elemento de cada par. Esses nmeros designam, na realidade, uma codicao numrica da propriedade correspondente e encontram-se denidos pela especicao DXF (Drawing eXchange Format) inventada pela AutoDesk para permitir troca de informao entre aplicaes de CAD. Assim, o nmero 0 designa o tipo da entidade, o nmero 10 designa o ponto primrio da entidade (o centro do crculo, neste caso), etc. A Tabela 1 descreve o signicado de alguns dos cdigos denidos pela especicao DXF. Quando se pretende saber apenas uma das propriedades de uma entidade, por exemplo, o seu tipo, pouco prtico ter de a procurar visualmente numa lista razoavelmente grande como a que resulta de entget. Note-se que no basta aceder posio onde julgamos estar a propriedade pois essa posio no a mesma para todos os objectos (pois nem todos os objectos possuem as mesmas propriedades) nem necessariamente a mesma para diferentes verses do AutoCad. Naturalmente, sendo o resultado da funo entget uma lista de pares, nada nos impede de denir uma funo que processe essa lista procura da propriedade em questo. Para concretizarmos, consideremos que pretendemos denir uma funo que, a partir da lista de propriedades de uma entidade, nos diz qual o seu tipo, cujo cdigo DXF o nmero zero. Para isso, o mais simples aplicarmos recurso: Se o primeiro elemento do primeiro par da lista o nmero zero (o cdigo da propriedade tipo) ento o valor correspondente o segundo elemento desse par. Se o primeiro elemento do primeiro par da lista no o nmero zero, ento podemos recursivamente procurar no resto da lista. Com base neste raciocnio, eis um esboo da funo: 37

Cdigo -1 0 1 2 3-4 5 6 7 8 10 11-18 38 39 48 50-58 60 62 100 210

Signicado Nome (uma string que pode mudar de sesso para sesso) Tipo Texto primrio Nome dado pelo utilizador (presente apenas nalgumas entidades) Outros textos (presente apenas nalgumas entidades) Referncia(uma string que nunca muda) Tipo de linha (presente apenas nalgumas entidades) Estilo de texto (presente apenas nalgumas entidades) Nome do layer Ponto primrio (o centro para um crculo, ponto inicial para uma linha, etc) Outros pontos (presente apenas nalgumas entidades) Elevao (apenas para entidades com elevao diferente de zero) Espessura (apenas para entidades com espessura diferente de zero) Escala do tipo de linha (presente apenas nalgumas entidades) ngulos Visibilidade (opcional, 0 para visvel, 1 para invisvel) ndice da cor Marcador de subclasse Direco de extruso Tabela 1: Alguns dos cdigos DXF

38

(defun valor-propriedade-tipo (lista-assoc) (cond ((= 0 (car (car lista-assoc))) (cdr (car lista-assoc))) (t (valor-propriedade-tipo (cdr lista-assoc)))))

Um pequeno teste assegura-nos que a funo tem o comportamento desejado:


_$ (valor-propriedade-tipo (entget (entlast))) "CIRCLE"

Obviamente, se quisermos obter outra propriedade qualquer, podemos simplesmente fazer uma cpia da funo em que apenas mudamos o cdigo que pesquisado. No entanto, a cpia de uma funo geralmente sugere que existe uma generalizao que devia ser explicitamente denida. Neste caso, razoavelmente bvio que todas as funes que procuram por uma determinada propriedade numa lista de associaes tero a mesma estrutura e que as podemos generalizar numa s funo que recebe a propriade a procura como argumento. Assim, podemos escrever
(defun valor-propriedade (propriedade lista-assoc) (cond ((= propriedade (car (car lista-assoc))) (cdr (car lista-assoc))) (t (valor-propriedade propriedade (cdr lista-assoc)))))

Esta funo, infelizmente, contm um problema: tal como se pode ver na Tabela 1, nem todas as propriedades existem para todas as entidades. Isto implica que possvel a funo valor-propriedade ser invocada para procurar uma propriedade que no existe numa dada lista de associaes. Neste caso, a funo teria um comportamento incorrecto pois, uma vez que as invocaes recursivas vo operando sobre uma lista de propriedades sucessivamente mais pequena, chegar o momento em que essa lista ca vazia. Como a funo no detecta essa situao continuar a tentar aceder ao primeiro elemento da lista, numa altura em que a lista j no tem quaisquer elementos. Para resolver este problema basta detectarmos este caso e, caso ocorra, devolver um valor que signique que a propriedade no existe naquela lista. Em geral, a linguagem Lisp convenciona que, para essas situaes, o valor a devolver nil. Tendo em conta este raciocnio, a funo ca ento com a seguinte forma:
(defun valor-propriedade (propriedade lista-assoc) (cond ((null lista-assoc) nil) ((= propriedade (car (car lista-assoc))) (cdr (car lista-assoc))) (t (valor-propriedade propriedade (cdr lista-assoc)))))

39

_$ (valor-propriedade 0 (entget (entlast))) "CIRCLE" _$ (valor-propriedade 10 (entget (entlast))) (10.0 20.0 0.0) _$ (valor-propriedade 123456789 (entget (entlast))) nil

4.2

A funo assoc

A funo valor-propriedade to til que at j se encontra pr-denida em Auto Lisp com o nome assoc mas com uma pequena diferena destinada a facilitar a distino entre no existir uma propriedade numa lista de associaes e existir essa propriedade mas com o valor nil: ao invs de devolver o valor da propriedade, a funo assoc devolve o par propriedade/valor, quando existe, ou nil quando no existe. Como um par necessariamente diferente de nil, ento possvel distinguir os dois casos. O reverso da moeda que, para aceder ao valor da propriedade torna-se ainda necessrio usar a funo cdr. Este comportamento visvel no seguinte exemplo:
_$ (assoc 0 (entget (entlast))) (0 . "CIRCLE") _$ (cdr (assoc 0 (entget (entlast)))) "CIRCLE"

Finalmente, podemos usar esta funo como base para a construo de selectores que, ao invs de operarem com a lista de propriedades de uma entidade, operam sobre a prpria entidade:
(defun tipo-entidade (entidade) (cdr (assoc 0 (entget entidade)))) (defun ponto-primario (entidade) (cdr (assoc 10 (entget entidade)))) _$ (tipo-entidade (entlast)) "CIRCLE" _$ (ponto-primario (entlast)) (10.0 20.0 0.0)

4.3

Modicao de Entidades

Vimos como aceder s propriedades de uma entidade. Vamos agora discutir a forma de modicarmos essas propriedades. A funo entmod permite a modicao de entidades. Para isso, a funo recebe como argumento uma lista de associaes contendo, para alm dos pares propriedade/valor que pretendemos modicar, um par nome/entidade contendo a entidade cujas propriedades pretendemos modicar. Por exemplo, se pretendermos mudar a origem do crculo criado anteriormente para as coordenadas (0, 0, 0) basta-nos avaliar a expresso:

40

_$ (entmod (list (cons -1 (entlast)) (cons 10 (xyz 0 0 0)))) ((-1 . <Entity name: 7ef91f30>) (10 0 0 0))

Quando bem sucedida, a invocao da funo entmod devolve a lista de associaes usada. Se ocorrer algum erro, a funo devolve nil. Como bvio no exemplo anterior, o uso da funo entmod pouco claro, sendo prefervel criarmos um modicador que esconda o seu uso.
(defun muda-ponto-primario (entidade novo-ponto-primario) (entmod (list (cons -1 entidade) (cons 10 novo-ponto-primario))))

Naturalmente, cada modicador deve corresponder a um selector. Por exemplo, para aceder e modicar a cr de um objecto, podemos denir as funes:
(defun cor (entidade) (cdr (assoc 62 (entget entidade)))) (defun muda-cor (entidade nova-cor) (entmod (list (cons -1 entidade) (cons 62 nova-cor))))

De igual modo, para aceder e modicar o raio de um crculo, podemos usar:


(defun raio (entidade) (cdr (assoc 40 (entget entidade)))) (defun muda-raio (entidade novo-raio) (entmod (list (cons -1 entidade) (cons 40 novo-raio))))

4.4

Criao de Entidades

H duas maneiras fundamentais de se criarem entidades em Auto Lisp:11 Atravs da pseudo-funo command. Esta funo recebe como argumentos as expresses necessrias que sero avaliadas apenas se e quando o AutoCad precisar dos valores correspondentes para criar a entidade. A criao da entidade ainda afectada por um conjunto de parmetros globais, como o layer em que se est a trabalhar, o UCS actual, o modo OSNAP, a lngua, etc. Para se usarem valores diferentes destes parmetros necessrio fazer as correspondentes alteraes previamente criao da entidade ou necessrio fazer posteriores alteraes entidade. O uso desta funo ainda dependente das alteraes que a AutoDesk vai fazendo sintaxe e parmetros dos comandos em sucessivas verses do AutoCad.
11 Na realidade, h trs maneiras fundamentais de se criarem objectos em AutoCad mas, por agora, apenas vamos lidar com duas.

41

Atravs da funo entmake. Esta funo recebe uma lista de associaes idntica que devolvida pela funo entget e usa os valores associados s propriedades correspondentes para criar e inicializar a entidade. As propriedades no obrigatrias cam com valores por omisso. A criao da entidade no afectada por quaisquer outros parmetros. Em particular, no existe dependncia do UCS actual, o que implica que as coordenadas das entidades criadas so interpretadas em termos do WCS. O uso desta funo ainda dependente das alteraes que a AutoDesk vai fazendo ao formato DXF em sucessivas verses do AutoCad. H ainda mais uma pequena diferena entre estas duas funes: em geral, usar entmake computacionalmente mais eciente (por vezes, muito mais eciente) que usar command, embora esta ltima possa ser mais simples para o programador. Uma vez que j vimos no passado a pseudo-funo command vamos agora discutir a entmake. Para exemplicar, consideremos a criao de um crculo de centro em (4, 4), raio 10 e cr 3 (verde). Como referimos anteriormente, temos de usar como argumento uma lista de associaes com os pares propriedade/valor pretendidos:
_$ (entmake (list (cons 0 "CIRCLE") (cons 62 3) (cons 10 (xy 4 4)) (cons 40 10))) ((0 . "CIRCLE") (62 . 3) (10 4 4) (40 . 10))

H duas importantes consideraes a fazer relativamente criao de entidades: O par que contm o tipo da entidade tem de ser o primeiro ou o segundo elemento da lista. Se for o segundo, o primeiro ter de ser o nome da entidade que ser simplesmente ignorado. Embora no o tenhamos feito no exemplo anterior, a especicao do marcador de subclasse tornou-se obrigatria em todas as verses recentes do AutoCad. O marcador de subclasse corresponde a duas ocorrncias do cdigo DXF 100, uma com a string AcDbEntity e outra com a classe de entidade (AcDbCircle, AcDbText, AcDbArc, AcDbLine, AcDbPoint, etc). Isso quer dizer que a expresso anterior deveria ter a seguinte forma:
(entmake (list (cons (cons (cons (cons (cons (cons 0 "CIRCLE") 100 "AcDbEntity") 100 "AcDbCircle") 62 3) 10 (xy 4 4)) 40 10)))

42

Contudo, para preservar a compatibilidade dos programas anteriores introduo do marcador de subclasse, existem vrios tipos de entidades em relao aos quais a funo entmake ignora a presena do marcador na lista de associaes.12 A prtica, nestes casos, nem sequer incluir as associaes com esse marcador. Seguindo esta prtica, apenas faremos referncia a este marcador quando tal for absolutamente necessrio. Se a operao entmake for bem sucedida, devolve a lista de associaes usada. Se for mal sucedida, devolve nil. Tal como acontecia com a funo entmod, a utilizao crua da funo entmake complexa, produzindo programas muito pouco legveis. Particularmente difcil de perceber o signicado dos nmeros (cdigos DXF) que representam propriedades. Para resolver este problema, semelhana do que zemos com os selectores e modicadores, podemos denir um construtor que aceite todos os parmetros da criao do crculo, escondendo assim a utilizao dos cdigos DXF. Infelizmente, no caso do construtor existem demasiados parmetros e vrios deles so opcionais. lgico que tenhamos de especicar o centro e o raio de um crculo mas a cor, o layer, o tipo de linha, etc, s sero necessrios em casos raros. Seria um incmodo para o utilizador do construtor ter de passar todos esses parmetros.

4.5

Listas de Propriedades

Para resolver estes problemas vamos seguir uma abordagem diferente. Ao invs de aceitar um nmero xo de argumentos, o construtor de crculos aceitar uma lista de argumentos de tamanho varivel. A ideia ser poder criar um crculo da seguinte forma:13
(cria-circulo (list centro: (xy 10 5) raio: 25 cor: 5))

H dois aspectos importantes a salientar do exemplo anterior. O primeiro que, ao invs de usarmos os cdigos numricos DXF, usmos nomes lgicos facilmente compreensveis, como centro:, raio: e cor:. O segundo que, ao invs de usarmos uma lista de associaes em que cada associao um par propriedade/valor, da forma ((p0 . v0 ) (p1 . v1 ) ... (pn . vn )), usmos uma lista simples contendo uma sucesso de propriedades e valores, da forma (p0 v0 p1 v1 ... pn vn ). A este tipo de lista d-se o nome de lista de propriedades ou, abreviadamente, plist.
A lista de marcadores ignorados inclui, entre outros, AcDbText, AcDb2dVertex, AcDb3dPolylineVertex, AcDbPolygonMeshVertex, AcDbPolyFaceMeshVertex, AcDbFaceRecord, AcDb2dPolyline, AcDb3dPolyline, AcDbArc, AcDbCircle, AcDbLine, AcDbPoint, AcDbFace, AcDbPolyFaceMesh, AcDbPolygonMesh, AcDbSolid. 13 Noutros dialectos de Lisp (mas no no Auto Lisp) possivel dispensar a invocao da funo list pois possivel denir funes com um nmero varivel de argumentos que constroem a lista de argumentos automaticamente.
12

43

Uma lista de propriedades muito semelhante a uma lista de associaes. A diferena fundamental est no facto de as associaes estarem espalmadas na prpria lista. Isso faz com que as listas de propriedades sejam mais legveis do que as listas de associaes. No entanto, trivial converter listas de propriedades em listas de associaes. Mais uma vez, o pensamento recursivo ajuda: se a lista de propriedades uma lista vazia, ento a lista de associaes correspondente tambm uma lista vazia. Caso contrrio, formamos um par com os dois primeiros elementos da lista de propriedades (para formar uma associao) e juntamos esse par ao resultado de converter a lista sem esses dois elementos.
(defun alist<-plist (plist) (if (null plist) (list) (cons (cons (car plist) (cadr plist)) (alist<-plist (cddr plist)))))

Do mesmo modo, trivial converter listas de associaes em listas de propriedades. O raciocnio idntico:
(defun plist<-alist (alist) (if (null alist) (list) (cons (caar alist) (cons (cdar alist) (plist<-alist (cdr alist))))))

A partir de uma lista de propriedades com os argumentos pretendidos para a criao da gura geomtrica temos agora de lhe juntar o tipo pretendido: (0 . "CIRCLE") no caso de crculos, (0 . "LINE") no caso de linhas, etc. Para evitar ter de escrever sempre o mesmo programa, vamos primeiro generalizar um pouco denindo uma funo que recebe o tipo de entidade (i.e., a string correspondente) e a lista de propriedades e invoca a funo entmake com a converso dessa lista de propriedades para o formato de lista de associaes requerido:
(defun cria-entidade (tipo params) (entmake (cons (cons 0 tipo) (alist<-plist params))))

A funo anterior permite criar entidades arbitrrias. agora trivial denir a funo que cria crculos:
(defun cria-circulo (params) (cria-entidade "CIRCLE" params))

Finalmente, apenas falta denir os nomes lgicos correspondentes s propriedades. Obviamente, o valor associado a cada nome lgico o cdigo DXF correspondente. Para um crculo, temos:

44

(setq centro: 10 raio: 40)

Outras propriedades so de uso mais geral:


(setq tipo-linha: 6 cor: 62 layer: 8 espessura: 39)

Para algumas propriedades faz tambm sentido dar nomes lgicos aos possveis valores:
(setq visibilidade: 60 visivel 0 invisivel 1)

Desta forma, podemos criar guras geomtricas de forma muito mais legvel. Por exemplo, para criar um crculo invisvel basta-nos escrever:
(cria-circulo (list centro: (xy 1 2) raio: 5 visibilidade: invisivel))

Podemos agora denir mais umas guras geomtricas, bem como as propriedades que lhes esto associadas. Por exemplo, para um arco de crculo podemos reutilizar as propriedades do crculo centro: e raio: e acrescentar mais algumas especcas dos arcos:
(defun cria-arco (params) (cria-entidade "ARC" params)) (setq arco-angulo-inicial: 50 arco-angulo-final: 51)

Para uma linha temos:


(defun cria-linha (params) (cria-entidade "LINE" params)) (setq ponto-inicial: 10 ponto-final: 11)

Note-se, no programa anterior, que o nome ponto-inicial tem o mesmo valor que o nome centro. Obviamente, ambos designam aquilo que anteriormente denominmos de ponto primrio mas muito mais claro distinguir os diferentes usos. Uma outra entidade que pode vir a ser til a elipse. Para alm da necessidade de denio de constantes para as suas propriedades especcas, a entidade elipse no dispensa a utilizao do marcador de classe AcDbEllipse, tal como referimos na seco 4.4. Assim, temos: 45

(setq marcador-subclasse: 100 extremidade-eixo-maior: 11 eixo-menor/eixo-maior: 40 elipse-angulo-inicial: 41 elipse-angulo-final: 42) (defun cria-elipse (params) (cria-entidade "ELLIPSE" (append (list marcador-subclasse: "AcDbEntity" marcador-subclasse: "AcDbEllipse") params)))

Note-se que acrescentmos (na posio correcta) as duas ocorrncias da propriedade 100 relativa ao marcador de subclasse. Note-se ainda que a propriedade extremidade-eixo-maior: pressupe um ponto relativamente ao referncial do centro da elipse.
Exerccio 4.5.1 Dena uma funo flor capaz de desenhar uma or custa da sobreposio de elipses rodadas, tal como se apresenta na seguinte imagem:

A funo flor dever receber, como parmetros, as coordenadas do centro da elipse, o comprimento do semi-eixo maior e do semi-eixo menor e o nmero de elipses a desenhar de forma a completar uma revoluo. Por exemplo, dever ser possvel gerar as imagens anteriores atravs da avaliao das seguintes expresses:
(flor (flor (flor (flor (flor (flor (xy (xy (xy (xy (xy (xy 0.0 2.5 5.0 0.0 2.5 5.0 0.0) 0.0) 0.0) 2.5) 2.5) 2.5) 1 1 1 1 1 1 0.5 0.5 0.5 0.2 0.5 0.8 10) 20) 30) 6) 6) 6)

Finalmente, para vermos um exemplo de uma entidade bastante parameterizvel, consideremos a criao de textos. Uma consulta documentao DXF do AutoCad revela vrias propriedades que podemos denir da seguinte forma: 46

(defun cria-texto (params) (cria-entidade "TEXT" params)) (setq texto: 1 estilo: 7 altura: 40 rotacao: 50 escala-x: 41 inclinacao: 51 geracao: 71 normal 0 contrario 2 invertida 4 alinhamento-horizontal: 72 ah-esquerda 0 ah-centro 1 ah-direita 2 ah-alinhado 3 ah-meio 4 ah-justificado 5 alinhamento-vertical: 73 av-linha-base 0 av-baixo 1 av-meio 2 av-topo 3)

Os parmetros alinhamento-horizontal e alinhamento-vertical permitem produzir variaes na forma como um dado texto se apresenta em relao a dois pontos. A Figura 12 mostra essas possveis variaes. Para se visualizar a utilizao desta ltima funo, a Figura 13 representa a imagem gerada pela seguinte expresso:
(foreach fraccao (enumera -0.5 0.5 0.1) (cria-texto (list ponto-inicial: (xy 0 (* 200 fraccao)) inclinacao: (* -2 fraccao) texto: "Inclinado" altura: 10)))

Uma outra imagem interessante a apresentada na Figura 14 que demonstra a capacidade do AutoCad para aplicar rotaes ao texto a inserir:
(foreach fraccao (enumera 0.0 1.0 0.1) (cria-texto (list ponto-inicial: (xy 0 0) rotacao: (* fraccao 2 pi) texto: "Rodado" altura: 10)))

47

Figura 12: Possibilidades de posicionamento de texto em relao a dois pontos. De baixo para cima o parmetro alinhamento-vertical toma os valores de 0 a 4. Da esquerda para a direita, o parmetro alinhamento-horizontal toma os valores de 0 a 6.

Figura 13: Um texto inclinado.

48

Figura 14: Um texto rodado.

Figura 15: Um texto rodado e com tamanho progessivamente decrescente. Finalmente, na Figura 15 apresentamos uma combinao entre sucessivas rotaes do texto e redues no tamanho da letra. A imagem foi gerada pela expresso:
(foreach fraccao (enumera 0.0 1.0 0.1) (cria-texto (list ponto-inicial: (xy 0 0) rotacao: (* fraccao 2 pi) texto: "Rodado" altura: (* (- 1 fraccao) 10))))

Exerccio 4.5.2 Embora os exemplos anteriores mostrem que a criao de entidades razoavelmente simples, nem todos os tipos de entidades possuem esta caracterstica. A criao de um texto multilinha (tipo MTEXT) um exemplo de uma entidade cuja especicao DXF complexa. Para alm de obrigar a lista de associaes a incluir a presena do marcador de subclasse para textos multilinha (AcDbMText), existe ainda

49

a necessidade de partir o texto em subtextos com, no mximo 250 caracteres. Isso claro a partir da especicao desta entidade que, para os cdigos DXF 1 e 3, arma: 1 String de texto. Se a string tiver 250 (ou menos) caracteres, todos aparecem no cdigo 1. Caso contrrio, a string dividida em pores de 250 caracteres que aparecem associadas a um ou mais cdigos 3. Se for usado algum cdigo 3, a ltima poro ca associada ao cdigo 1 e possui menos de 250 caracteres.14 3 Texto adicional (em pores de 250 caracteres) (opcional) Esta documentao mostra que a criao de um texto multilinha implica particionar o texto em subfragmentos que devero ser agrupados na lista de associaes conjuntamente com os cdigos 1 e 3. Tendo este comportamento em conta, dena a funo cria-texto-multilinha que, a partir de uma string e de uma lista de propriedades, particiona a string e acrescenta as associaes necessrias lista de propriedades para a correcta criao da entidade. Exerccio 4.5.3 Consulte a documentao DXF do AutoCad para splines e dena uma funo cria-spline bem como as constantes que considerar relevantes de modo a permitir a fcil criao de curvas spline.

4.6

Leitura e Modicao de Entidades

A partir do momento em que associmos nomes s propriedades podemos tirar partido disso para redenir as operaes que obtm e modicam o valor das propriedades das entidades grcas:
(defun obtem-propriedade (entidade propriedade) (cdr (assoc propriedade (entget entidade)))) (defun muda-propriedade (entidade propriedade novo-valor) (entmod (list (cons -1 entidade) (cons entidade novo-valor))))

Usando estas operaes, podemos tambm redenir alguns dos selectores e modicadores que tnhamos introduzido anteriormente. Por exemplo, para a cr, temos:
(defun cor (entidade) (obtem-propriedade entidade cor:)) (defun muda-cor (entidade nova-cor) (muda-propriedade entidade cor: nova-cor)) Exerccio 4.6.1 Dena a funo area-entidade-circulo que, dada uma entidade do tipo circulo com um raio r, calcula a sua rea atravs da frmula A = r2 . Exerccio 4.6.2 Dena a funo area-entidade-elipse que, dada uma entidade do tipo elipse, calcula a sua rea. Dada uma elipse de semi-eixos a e b, a sua rea A dada por A = ab.
Na verdade, a documentao DXF do AutoCad apenas refere os casos de strings com menos de 250 caracteres e de strings com mais de 250 caracteres, deixando na dvida o que acontece no caso de strings com exactamente 250 caracteres. Por experimentao verica-se que este caso tambm ca associado ao cdigo DXF 1.
14

50

4.7

Eliminao de Entidades

Para se eliminar uma entidade, o AutoCad disponibiliza a funo entdel que recebe, como argumento, a entidade a eliminar. Se a entidade j tiver sido eliminada e o desenho a que ela pertencia ainda no tiver sido encerrado, a invocao desta funo repe a entidade no desenho. Para se determinar se uma entidade est eliminada, podemos usar a funo entget com a entidade em questo como argumento. Se essa entidade estiver eliminada, a funo entget devolver nil. Este comportamente visvel na seguinte interaco com o Auto Lisp:
_$ (cria-circulo ;;criamos um circulo (list centro: (xy 1 2) raio: 10)) ((0 . "CIRCLE") (10 1 2) (40 . 10)) _$ (setq circulo (entlast)) ;;guardamo-lo numa variavel <Entity name: 7efa26d8> _$ (entget circulo) ;;obtemos as suas propriedades ((-1 . <Entity name: 7efa26d8>) (0 . "CIRCLE") ...) _$ (entdel circulo) ;;eliminamo-lo <Entity name: 7efa26d8> _$ (entget circulo) ;;obtemos as suas propriedades de novo nil ;;e nada recebemos _$ (entdel circulo) ;;"deseliminamo-lo" <Entity name: 7efa26d8> _$ (entget circulo) ;;obtemos as suas propriedade de novo ((-1 . <Entity name: 7efa26d8>) (0 . "CIRCLE") ...)

4.8

ltima Entidade

No passado dissemos que a funo entlast devolve a ltima entidade criada. Podemos agora, mais correctamente, dizer que esta funo devolve a ltima entidade no eliminada, tal como se pode ver no seguinte exemplo:
_$ (cria-circulo (list centro: (xy 1 2) raio: 10)) ((0 . "CIRCLE") (10 1 2) (40 . 10)) _$ (tipo-entidade (entlast)) "CIRCLE" _$ (cria-linha (list ponto-inicial: (xy 3 4) ponto-final: (xy 5 6))) ((0 . "LINE") (10 3 4) (11 5 6)) _$ (tipo-entidade (entlast)) "LINE" _$ (entdel (entlast)) <Entity name: 7efcaab8> _$ (tipo-entidade (entlast)) "CIRCLE"

4.9

Iterao de Entidades

O AutoCad disponibiliza ainda uma operao para iterar as entidades de um desenho: entnext. 51

Quando invocada sem argumentos, a funo devolve a primeira entidade no eliminada (ou nil, se no existir nenhuma). Quando invocada com uma entidade como argumento, a funo devolve a entidade (no eliminada) seguinte (ou nil se o argumento for a ltima entidade no eliminada). Tal como se v no seguinte exemplo, este comportamento permite iterar ao longo de todas as entidades do desenho:
_$ (command "_.erase" "_all" "") nil _$ (entnext) nil _$ (cria-circulo (list centro: (xy 1 2) raio: 10)) ((0 . "CIRCLE") (10 1 2) (40 . 10)) _$ (cria-linha (list ponto-inicial: (xy 3 4) ponto-final: (xy 5 6))) ((0 . "LINE") (10 3 4) (11 5 6)) _$ (entnext) <Entity name: 7efcaac0> _$ (entnext (entnext)) <Entity name: 7efcaac8> _$ (entnext (entnext (entnext))) nil

Como base nesta funo podemos agora construir outras. Por exemplo, dada uma entidade podemos coleccionar numa lista todas as entidades que vm a partir dela.
(defun entidades-seguintes (actual) (if (null actual) (list) (cons actual (entidades-seguintes (entnext actual)))))

A partir desta funo podemos agora trivialmente coleccionar todas as entidades presentes no desenho actual.
(defun todas-entidades () (entidades-seguintes (entnext)))

Continuando o exemplo anterior, temos:


_$ (todas-entidades) (<Entity name: 7efcaac0> <Entity name: 7efcaac8>)

4.10

Entidades Compostas

At agora vimos que cada gura geomtrica que criamos no AutoCad possui uma entidade associada. No entanto, algumas das guras geomtricas 52

disponibilizadas pelo AutoCad so demasiadamente complexas para poderem ser representadas por uma nica entidade. Por exemplo, uma linha poligonal a trs dimenses15 necessita no s de uma entidade para representar as caractersticas gerais da linha como tambm necessita de uma sucesso de entidades para representar a sucesso dos vrtices da linha. Por ser composta por vrias subentidades, uma linha poligonal diz-se uma entidade composta. Aps a criao de uma linha poligonal, embora a entidade que a representa seja acedvel usando a funo entlast, para se aceder s entidades que representam os vrtices necessrio iterar a partir da primeira, usando a funo entnext, tal como perceptvel na seguinte interaco:16
_$ (command "_.3DPOLY" (xy -1 -1 0) (xy 1 0 1) (xy 0 1 -1) "") nil _$ (entget (entlast)) ((-1 . <Entity name: 7efcb1f0>) (0 . "POLYLINE") ... (10 0.0 0.0 0.0) ...) _$ (entget (entnext (entlast))) ((-1 . <Entity name: 7efcb1f8>) (0 . "VERTEX") ... (10 -1.0 -1.0 0.0) ...) _$ (entget (entnext (entnext (entlast)))) ((-1 . <Entity name: 7efcb200>) (0 . "VERTEX") ... (10 1.0 0.0 1.0) ...) _$ (entget (entnext (entnext (entnext (entlast))))) ((-1 . <Entity name: 7efcb208>) (0 . "VERTEX") ... (10 0.0 1.0 -1.0) ...) _$ (entget (entnext (entnext (entnext (entnext (entlast)))))) ((-1 . <Entity name: 7efcb210>) (0 . "SEQEND") ...) _$ (entnext (entnext (entnext (entnext (entnext (entlast)))))) nil

Repare-se nos tipos das entidades que so acedidas: embora a primeira seja do tipo POLYLINE, as seguintes so do tipo VERTEX e, nalmente, a ltima do tipo SEQEND. Esta ltima entidade usada para marcar o m da sequncia de entidades que constituem a entidade composta. Com base nesta informao estamos em condies de denir funes que processem as linhas poligonais. Por exemplo, para sabermos quais so as coordenadas dos vrtices de uma linha poligonal, podemos simplesmente comear com a entidade que representa a linha e ir recursivamente recolhendo as coordenadas das entidades seguintes at atingir a entidade cujo tipo SEQEND:
Verses recentes do AutoCad permitem um outro topo de linha poligonal denominada linha poligonal leve que representada por uma nica entidade. Esta linha, contudo, estritamente bidimensional e no permite o mesmo grau de sosticao das linhas poligonais 3D. 16 Para facilitar a leitura, omitimos alguma da informao produzida.
15

53

(defun junta-vertices (entidade) (if (= (tipo-entidade entidade) "SEQEND") () (cons (ponto-primario entidade) (junta-vertices (entnext entidade))))) (defun vertices-linha-3d (linha) (junta-vertices (entnext linha)))

agora bastante mais simples obter as coordenadas que denem a linha poligonal:
_$ (vertices-linha-3d (entlast)) ((-1.0 -1.0 0.0) (1.0 0.0 1.0) (0.0 1.0 -1.0))

Para se criar uma linha poligonal, necessrio usar o processo inverso. Dada a lista de coordenadas dos vrtices da linha a criar, criamos a entidade que representa a linha poligonal e iteramos ao longo da lista, criando um vrtice para cada elemento. Finalmente, terminamos com a entidade que marca o m da sequncia. Para tornar a especicao das propriedades mais clara, conveniente denirmos previamente algumas constantes:
(setq tipo-linha: 70 linha-aberta 0 linha-fechada 1 linha-3d 8 malha-poligonal-3d 16 tipo-vertice: 70 vertice-extra 1 vertice-tangente 2 vertice-spline 8 vertice-controlo 16 vertice-3d 32 vertice-malha 64)

A linha poligonal pode agora ser criada usando a seguinte funo:


(defun cria-linha-3d (vertices) (cria-entidade "POLYLINE" (list tipo-linha: polilinha-3d)) (foreach vertice vertices (cria-entidade "VERTEX" (list centro: vertice tipo-vertice: vertice-3d))) (cria-entidade "SEQEND" (list)))

Note-se que a funo anterior apenas cria um tipo especco de linha poligonal.

54

4.11

Redesenho de Entidades

H uma funo relacionada com entidades que embora no seja geralmente necessria, pode vir a ser til: entupd. Esta funo recebe uma entidade como argumento e redesenha-a no ecran. Em geral, nunca preciso usar esta funo pois o AutoCad encarrega-se de redesenhar automaticamente as entidades que mudam. No entanto, no caso de entidades cujo processo de modicao moroso (como os vrtices de uma linha poligonal a trs dimenses), o AutoCad no faz o redesenho automaticamente. Nestes casos, ca responsabilidade do programador invocar a funo entupd sobre a entidade modicada.

4.12

Criao de Linhas Poligonais Leves

As linhas poligonais descritas na seco 4.10 so entidades complexas compostas por vrias subentidades. Nesta seco vamos ver uma variante mais ligeira dessas linhas poligonais que usada pelo AutoCad sempre que lida com linhas poligonais bidimensionais. Contrariamente a uma linha poligonal genrica, uma linha poligonal leve uma nica entidade. Os vrtices da linha poligonal so representados como mltiplas ocorrncias de uma mesma propriedade cujo cdigo DXF o nmero 10. Estes vrtices so relativos a um sistema de coordenadas prprio de cada entidade, denominado sistema de coordenadas do objecto (do Ingls Object Coordinate System ou, abreviadamente, OCS). Sempre que se pretender conhecer as coordenadas dos vrtices da linha poligonal relativamente ao sistema de coordenadas do mundo (do Ingls World Coordinate System ou, abreviadamente, WCS) necessrio fazer uma transformao de coordenadas, implementada pela funo trans. Para construirmos uma destas linhas vamos denir a seguinte funo:
(defun cria-polilinha-leve (params) (cria-entidade "LWPOLYLINE" params))

Infelizmente, no caso de polilinhas leves, a construo mais complexa do que anteriormente pois, por um lado, no possvel omitir o marcador de subclasse (ver seco 4.4) e, por outro, necessrio associar um cdigo DXF a cada vrtice. Por exemplo, para criarmos um rectngulo com os vrtices nas coordenadas (10, 20), (20, 20), (20, 40) e (10, 40), teremos de avaliar a seguinte expresso:
(cria-polilinha-leve (list 100 "AcDbEntity" 100 "AcDbPolyline" 70 1 90 4 10 (xy 10 20) 10 (xy 20 20) 10 (xy 20 40) 10 (xy 10 40))) ;;marcador de subclasse ;;idem ;;tipo de linha fechada ;;numero de vertices ;;vertice ;;idem ;;idem ;;idem

55

Uma vez que incmodo termos de especicar sistematicamente os marcadores de subclasse e o nmero de vrtices, vamos redenir a funo anterior de modo a que ela aceite tambm a seguinte sintaxe para criar o mesmo quadrado:
(cria-polilinha-leve (list vertices: (list (xy 10 20) (xy 20 20) (xy 20 40) (xy 10 40)) tipo-linha: linha-fechada))

A ideia consiste em inventar uma nova opo vertices: (com um cdigo que no interra com os cdigos DXF pr-denidos) e, sempre que ele aparece, processamos a lista que lhe est associada de modo a gerar o cdigo DXF correcto para cada vrtice. Note-se que os restantes parmetros no devem ser afectados, permitindo misturar as vrias formas de especicar os vrtices. Por exemplo, se a lista de parmetros for (10 p0 70 1 -1000 (p1 p2 p3 ) 10 p4 ), a sua converso ir produzir a lista (10 p0 70 1 10 p1 10 p2 10 p3 10 p4 ). Este comportamento est incorporado no seguinte programa onde comeamos por denir as constantes relevantes:
(setq vertices: -1000 numero-vertices: 90 vertice: 10) (defun converte-parametros (params) (cond ((null params) ()) ((= (car params) vertices:) (append (converte-vertices (cadr params)) (converte-parametros (cddr params)))) (t (cons (car params) (cons (cadr params) (converte-parametros (cddr params))))))) (defun converte-vertices (vertices) (if (null vertices) () (cons vertice: (cons (car vertices) (converte-vertices (cdr vertices))))))

Para sabermos o nmero de vrtices precisamos agora de contabilizar as ocorrncias do cdigo associado a vertice:.
(defun conta-parametros-vertice (params) (cond ((null params) 0) ((= (car params) vertice:) (1+ (conta-parametros-vertice (cddr params)))) (t (conta-parametros-vertice (cddr params)))))

56

Finalmente, podemos combinar tudo numa s funo:


(defun cria-polilinha-leve (params) (setq params (converte-parametros params)) (cria-entidade "LWPOLYLINE" (append (list marcador-subclasse: "AcDbEntity" marcador-subclasse: "AcDbPolyline" numero-vertices: (conta-parametros-vertice params)) params)))

Para vermos um exemplo da utilizao desta funo, consideremos o desenho de polgonos que foi descrito na seco 3. Na altura, denimos uma funo que, a partir das coordenadas dos vrtices do polgono, desenhava-o como uma sucesso de linhas:
(defun poligono (vertices) (command "_.line") (foreach vertice vertices (command vertice)) (command "_close"))

Admitindo que todas as coordenadas so bi-dimensionais, podemos redenir a funo de modo a usar a criao de polilinhas leves:
(defun poligono (vertices) (cria-polilinha-leve (list tipo-linha: linha-fechada vertices: vertices)))

Obviamente, com esta nova denio todos os exemplos que foram apresentados na seco 3 continuam a produzir os mesmos resultados. A diferena estar apenas na performance e no facto de a utilizao de entidades no obedecer aos parmetros que afectam o command, nomeadamente, o UCS, o SNAP, etc.

4.13

Manipulao de Linhas Poligonais Leves

Para alm de podermos construir linhas poligonais leves, podemos ter a necessidade de as manipular. Para a maioria das propriedades como, por exemplo, o tipo-linha:, podemos continuar a usar o selector obtem-propriedade. No entanto, para obtermos os vrtices da linha poligonal leve (no referencial OCS) temos de empregar uma funo diferente da obtem-propriedade pois esta apenas devolve o valor associado primeira ocorrncia da propriedade na lista de associaes enquanto que agora precisamos de construir uma lista com todas as ocorrncias da propriedade vertice:. Assim, temos:

57

(defun valores-propriedade (propriedade lista-assoc) (cond ((null lista-assoc) ()) ((= propriedade (caar lista-assoc)) (cons (cdar lista-assoc) (valores-propriedade propriedade (cdr lista-assoc)))) (t (valores-propriedade propriedade (cdr lista-assoc)))))

Usando esta funo agora trivial denir:


(defun vertices-polilinha-leve (entidade) (valores-propriedade vertice: (entget entidade)))

4.14

Entidades Degeneradas

At agora, j vimos duas formas diferentes de criar entidades geomtricas no AutoCad: uma, usando a funo command e, outra, usando a funo entmake. Na seco 4.4 discutimos as diferenas fundamentais entre estas duas operaes. Houve uma diferena, contudo, que ainda no discutimos e que est relacionada com a criao de entidades degeneradas como, por exemplo, um crculo de raio zero. Frequentemente, a criao dessas entidades um erro mas, nalguns casos, precisamente o que se pretende. Em particular, quando a criao das entidades feita por processos algortmicos, perfeitamente possvel que se atinjam situaes em que nos interesse criar entidades degeneradas. Esta situao congura uma ltima e importante diferena entre a criao de entidades via command e entmake: por uma questo de convenincia, os comandos encontram-se protegidos contra a criao de entidades degeneradas, enquanto que a funo entmake no limita os valores das propriedades das entidades criadas. Esta diferena visvel no seguinte exemplo de criao de um crculo degenerado:
_$ (command "_.CIRCLE" (xyz 1 1 1) 0) ; error: Function cancelled

Por outro lado, usando os mecanismos que desenvolvemos e que se baseiam na funo entmake, temos:
_$ (cria-circulo (list centro: (xyz 1 1 1) raio: 0)) ((0 . "CIRCLE") (10 1 1 1) (40 . 0))

Assim, quando exista a possibilidade (desejada) de criao de entidades degeneradas, prefervel usar mecanismos baseados na funo entmake a usar mecanismos baseados na pseudo-funo command. Infelizmente, nem tudo o que se consegue fazer com a pseudo-funo command igualmente fcil de obter com a funo entmake. Em particular, a criao de

58

slidos tri-dimensionais substancialmente dicultada pelo facto de a codicao DXF destes slidos ser proprietria do AutoCad. Mais frente veremos um terceiro mtodo de criao de entidades geomtricas que resolve estes problemas.

4.15

Seleco Interactiva de Entidades

Vimos que a funo entlast permite aceder ltima entidade criada e que a funo entnext permite iterar todas as entidades. Infelizmente, estas duas funes no so sucientes para permitir identicar uma entidade escolhida pelo utilizador. Quando necessrio pedir ao utilizador que seleccione uma entidade presente num desenho pode-se empregar a funo entsel que recebe, opcionalmente, um texto a apresentar ao utilizador. Quando invocada, a funo comea por apresentar o texto e, de seguida, aguarda que o utilizador use o rato. Quando o utilizador clica num objecto grco qualquer, a funo devolve uma lista contendo, como primeiro elemento, a entidade seleccionada e, como segundo elemento, as coordenadas do ponto onde o utilizador clicou.
_$ (entsel "Escolha uma entidade") (<Entity name: 7efcaac0> (7.04109 -5.97352 0.0))

A razo porque devolvido tambm o ponto clicado prende-se com o facto de podermos usar essa informao para, de alguma forma, modicarmos a entidade. Por exemplo, se pretendermos partir uma linha em duas num dado ponto, podemos denir a seguinte funo:
(defun parte-linha (linha ponto) (cria-linha (list ponto-inicial: (obtem-propriedade linha ponto-inicial:) ponto-final: ponto)) (cria-linha (list ponto-inicial: ponto ponto-final: (obtem-propriedade linha ponto-final:))) (entdel linha))

Podemos agora usar a funo entsel para obter a linha que pretendemos partir em duas e o ponto nessa linha onde a queremos partir, passando esses valores separadamente para a funo parte-linha. isso que a funo seguinte faz:
(defun comando-parte-linha (/ seleccao) (setq seleccao (entsel)) (parte-linha (car seleccao) (cadr seleccao)))

59

4.16

Denio de Comandos AutoCad

At agora, todas as operaes que denimos apenas podiam ser invocadadas a partir do Auto Lisp. Para quem gosta de alisp, isso suciente, mas para o utilizador menos experiente de AutoCad isso pode rapidamente tornar-se inconveniente: na maioria dos casos ser prefervel poder invocar funes sem abandonar o ambiente do AutoCad e sem sequer ter se saber a sintaxe do Lisp. Para isso, o Auto Lisp permite a denio de comandos AutoCad. Essa denio feita de forma idntica denio de funes, sendo a distino feita apenas por uma conveno de nomes: para se denir o comando nome, basta denir uma funo cujo nome ser c:nome e cujo corpo ir conter as aces que se pretende que o comando realize. Uma vez que a funo se destina a ser invocada como se de um comando AutoCad se tratasse, ela no pode receber argumentos e, consequentemente, no pode ter parmetros. No entanto, embora se destine fundamentalmente a ser usada no AutoCad, o comando denido no deixa de estar implementado por uma funo Lisp e, por isso, pode tambm ser invocado a partir do Lisp. semelhana de qualquer outra invocao de funo, o comando nome assim denido pode ser invocado como (c:nome). Note-se que o nome da funo, em Auto Lisp, aquele que foi empregue na denio. apenas para o AutoCad que o nome perde o prexo c:. Na denio de comandos, importante termos em conta que se o comando a denir tiver o mesmo nome que um comando pr-denido, ele s car activo quando indenirmos o comando pr-denido. Usando a denio de comandos AutoCad podemos tornar mais prtica a operao de partir uma linha:
(defun c:parte-linha (/ seleccao) (setq seleccao (entsel)) (parte-linha (car seleccao) (cadr seleccao)))

Note-se que a funo anterior no recursiva: a funo c:parte-linha invoca a funo parte-linha mas trata-se de duas funes diferentes. No entanto, com a denio anterior ca disponvel no AutoCad um novo comando denominado parte-linha que, quando invocado, provoca a invocao da funo c:parte-linha que, por sua vez, invoca a funo parte-linha.

4.17

Obteno de Informao

Vimos que a funo entsel permite pedir ao utilizador para seleccionar uma entidade, entidade essa que ser devolvida juntamente com as coordenadas do ponto clicado. Em muitos casos, contudo, no estamos interessados em obter entidades mas sim outro tipo de informao. Por exemplo, podemos estar apenas interessados em pedir ao utilizador que indique pontos na sua rea de desenho. Outra possibilidade ser estarmos interessados em pedir nmeros ou textos ou nomes de cheiros. Para cada um destes ns, o Auto Lisp disponibiliza uma funo pr-denida: getpoint, getcorner, getint, getreal, getstring, getfiled, etc. Este 60

Funo getint getreal getstring getpoint getcorner getdist getangle

getorient

Tipo de Resultado Um inteiro. Um real. Uma string. Um ponto obtido na rea de desenho ou na linha de comandos. Um ponto relativo a um outro ponto dado, obtido na rea de desenho ou na linha de comandos. Uma distncia obtida na rea de desenho ou na linha de comandos. Um ngulo (em radianos) obtido na rea de desenho ou na linha de comandos. O ngulo zero dado pela varivel ANGBASE e cresce segundo ANGDIR. Um ngulo (em radianos) obtido na rea de desenho ou na linha de comandos. O ngulo zero sempre para a direita e cresce segundo ANGDIR.

Tabela 2: Funes de obteno de informao pr-denidas do Auto Lisp. conjunto de funes est descrito na Tabela 2. Qualquer delas recebe um argumento opcional que uma string a apresentar ao utilizador para o informar do que se pretende. ainda possvel restringir os valores que se podem obter a partir das funes anteriores atravs da utilizao da funo initget, por exemplo, proibindo os valores negativos ou limitando os pontos obtidos a terem uma coordenada z nula.

4.18

Exemplos

Vimos que a denio de funes Auto Lisp pode ser usada tambm para denir comandos AutoCad. Nesta seco vamos combinar a denio de comandos com a utilizao de funes que pedem informaes ao utilizador. A ttulo de exemplo, consideremos um comando para criar uma diviso rectangular em plantas de habitaes. O comando ir pedir a posio dos cantos da diviso e, com esses valores, ir construir o rectngulo correspondente e colocar no interior desse rectngulo uma linha de texto com a rea da diviso. Para implementarmos este comando vamos comear por denir a funo divisao-rectangular que, a partir do canto inferior esquerdo e do canto superior direito, constri o rectngulo em questo e insere o texto pretendido:
(defun divisao-rectangular (p0 p1 / meia-altura) (cria-polilinha-leve (list tipo-linha: linha-fechada vertices: (list p0 (xy (cx p1) (cy p0)) p1 (xy (cx p0) (cy p1))))) (setq meia-altura (/ (+ (cy p0) (cy p1)) 2.0))

61

Figura 16: Divises rectangulares com a rea ou a orientao do texto incorrectamente calculadas.
(cria-texto (list ponto-inicial: (xy (cx p0) meia-altura) ponto-final: (xy (cx p1) meia-altura) altura: 1 texto: (strcat " Area:" (rtos (* (- (cx p1) (cx p0)) (- (cy p1) (cy p0)))) " ") alinhamento-horizontal: ah-alinhado alinhamento-vertical: av-meio)))

Uma vez que tem parmetros, esta funo apenas pode ser invocada a partir do Auto Lisp. Isso suciente para testarmos o seu funcionamento mas no suciente para o seu uso a partir do AutoCad. O objectivo, agora, criar um comando que recolhe do utilizador os dados sucientes para conseguir invocar a funo anterior. Para evitar estarmos sempre a denir novos nomes, vamos reutilizar o nome da funo divisao-rectangular para baptizar o comando:
(defun c:divisao-rectangular (/ p0 p1) (setq p0 (getpoint "\nEspecifique o primeiro canto") p1 (getcorner "\nEspecifique o segundo canto" p0)) (divisao-rectangular p0 p1))

Infelizmente, a denio anterior tem um problema: apenas funciona correctamente se o segundo canto car acima e direita do primeiro canto. Esse problema visvel na Figura 16 onde acrescentmos tambm uma seta desde o primeiro ponto fornecido at o segundo ponto fornecido. Para corrigirmos o problema, necessitamos de processar os pontos recolhidos de forma a invocarmos a funo divisao-rectangular com o canto inferior esquerdo e com o canto superior direito do rectngulo virtual recolhido. precisamente isso que faz a seguinte redenio: 62

Figura 17: Divises rectangulares criadas com o comando AutoCad divisao-rectangular.


(defun c:divisao-rectangular (/ p0 p1) (setq p0 (getpoint "\nEspecifique o primeiro canto") p1 (getcorner "\nEspecifique o segundo canto" p0)) (divisao-rectangular (xy (min (cx p0) (cx p1)) (min (cy p0) (cy p1))) (xy (max (cx p0) (cx p1)) (max (cy p0) (cy p1)))))

Com esta correco agora possvel criar, interactivamente, a planta de uma habitao e anotar cada diviso criada com a informao da sua rea, tal como ilustramos na Figura 17. Embora o comando divisao-rectangular permita criar divises onde automaticamente calculada e escrita a sua rea, existem duas importantes limitaes: Apenas permite criar reas rectangulares alinhadas com os eixos x e y . No permite inserir a rea em guras geomtricas j existentes num desenho. Estas limitaes sugerem que repensemos o comando. A primeira limitao resolve-se de forma trivial: no vale a pena ser o comando a criar o rectngulo pois o AutoCad j possui uma operao para isso, denominada rectangle. Na realidade, o AutoCad j disponibiliza muitas operaes capazes de criar as mais variadas guras geomtricas, pelo que intil estarmos a tentar replicar essa funcionalidade. A segunda limitao um pouco mais difcil de resolver pois o clculo da rea e do ponto de insero dessa rea depende da gura geomtrica em questo, o que nos vai obrigar a considerar vrias formas diferentes de o fazermos.17
17

Verses recentes do AutoCad so capazes de determinar a rea e o centro de um vasto con-

63

Comecemos por analisar o clculo da rea de uma entidade geomtrica. Se a entidade for um crculo de raio r, a rea r2 mas se for uma elipse de semi-eixos a e b, a rea ab. Isto permite-nos denir as funes:
(defun area-entidade-circulo (entidade) (* pi (quadrado (obtem-propriedade entidade raio:)))) (defun area-entidade-elipse (entidade / eixo-maior) (setq eixo-maior (pol-ro (obtem-propriedade entidade extremidade-eixo-maior:))) (* pi eixo-maior (obtem-propriedade entidade eixo-menor/eixo-maior:) eixo-maior))

Para podermos tratar de forma genrica qualquer das duas entidades anteriores, podemos denir uma funo que distingue os dois casos:
(defun area-entidade (entidade / tipo) (setq tipo (tipo-entidade entidade)) (cond ((= tipo "CIRCLE") (area-entidade-circulo entidade)) ((= tipo "ELLIPSE") (area-entidade-elipse entidade))))

Quanto ao ponto de insero, vamos considerar que o texto ir car centrado na entidade, pelo que podemos denir uma operao genrica que distingue os vrios tipos de entidade:18
(defun centro-entidade (entidade / tipo) (setq tipo (tipo-entidade entidade)) (cond ((= tipo "CIRCLE") (obtem-propriedade entidade centro:)) ((= tipo "ELLIPSE") (obtem-propriedade entidade centro:))))

A partir do momento em que temos funes genricas que nos do a rea e o centro de uma entidade, podemos denir a funo (igualmente genrica) que, a partir de uma entidade, insere no seu centro um texto a descrever a sua rea:
(defun insere-area-entidade (entidade / centro) (setq centro (centro-entidade entidade)) (cria-texto (list ponto-inicial: centro ponto-final: centro
junto de entidades. No entanto, para efeitos pedaggicos, vamos admitir que essa funcionalidade no existe. 18 No caso dos crculos e elipses, seria possvel combinar as duas situaes numa s pois a propriedade para aceder ao centro da entidade a mesma. No entanto, para tornar o programa mais claro, vamos manter a separao.

64

altura: 1.5 texto: (strcat "Area:" (rtos (area-entidade entidade))) alinhamento-horizontal: ah-centro alinhamento-vertical: av-meio)))

Por m, podemos denir um comando no AutoCad que, aps pedir ao utilizador que seleccione uma entidade, invoca a funo anterior usando a entidade seleccionada como argumento:
(defun c:insere-area () (insere-area-entidade (car (entsel "Seleccione a entidade pretendida"))))

Como bvio, o comando anterior apenas pode ser aplicado a crculos e elipses, o que limita excessivamente a sua utilidade. Vamos, portanto, modiclo para lidar com outras entidades, por exemplo, rectngulos, pentgonos, etc. Uma caracterstica interessante do AutoCad que nos simplica muito o problema o facto de a maioria das guras geomtricas poligonais estarem implementadas usando o mesmo tipo de linhas poligonais que discutimos na seco 4.12. De facto, o AutoCad no possui um tipo de entidade especco para rectngulos ou pentgonos, optando por implement-los como sequncias de linhas poligonais leves, tal como vericvel nas seguintes interaces:
_$ (command "_.rectangle" (xy 1 2) (xy 3 4)) nil _$ (tipo-entidade (entlast)) "LWPOLYLINE" _$ (command "_.polygon" 5 (xy 1 2) "_inscribed" 3) nil _$ (tipo-entidade (entlast)) "LWPOLYLINE"

Assim, podemos preocupar-nos apenas em lidar com este tipo de entidades e, automaticamente, caremos habilitados a lidar com uma enorme variedade de guras geomtricas. Temos, portanto, de encontrar uma forma de calcular a rea e o centro de um qualquer polgono delimitado por linhas poligonais leves. Para calcular a rea de um polgono podemos empregar uma abordagem simples que foi inventada independentemente pelos matemticos Meister e Gauss em nais do sculo dezoito. A ideia consiste em decompor o polgono num conjunto de trapzios rectangulares, tal como se apresenta na Figura 18. Se for [(x0 , y0 ), (x1 , y1 ), (x2 , y2 ), . . . , (xn1 , yn1 ), (xn , yn )] a sequncia de coordenadas dos vrtices do polgono, o primeiro trapzio ter os vrtices em (x0 , 0), (x0 , y0 ), (x1 , y1 ) e (x1 , 0), o segundo em (x1 , 0), (x1 , y1 ), (x2 , y2 ) e (x2 , 0), o penltimo em (xn1 , 0), (xn1 , y0 ), (xn , yn ) e (xn , 0) e o ltimo trapzio ter os vrtices em (xn , 0), (xn , yn ), (x0 , y0 ) e (x0 , 0). A rea de cada um destes trapzios pode ser calculada pela frmula 1 A = (xi+1 xi )(yi+1 + yi) 2 65

(x1 , y1 )

(x0 , y0 )

x Figura 18: Decomposio de um polgono arbitrrio em trapzios rectangulares. sendo que a diferena xi+1 xi ser positiva ou negativa consoante xi menor ou maior que xi+1 , implicando que a rea do trapzio respectivo ser tambm positiva ou negativa. Ora sendo o polgono fechado, se percorrermos os vrtices por ordem, por exemplo, segundo os ponteiros do relgio, necessriamente que alguns dos vrtices tero coordenadas xi sucessivamente crescente enquanto outros a tero sucessivamente decrescente. No difcil vermos que ao somarmos os trapzios com rea positiva aos trapzios com rea negativa, iremos obter a rea total do polgono. Essa rea poder ser positiva ou negativa consoante a ordem escolhida para percorrer os vrtices do polgono mas, em qualquer caso, ser a rea total do polgono. Traduzindo este raciocnio para Lisp, podemos denir as seguintes funes que, a partir de uma lista com as coordenadas dos vrtices do poligono, calculam a sua rea:
(defun area-vertices (vertices) (if (null (cdr vertices)) 0 (+ (area-trapezio (car vertices) (cadr vertices)) (area-vertices (cdr vertices))))) (defun area-trapezio (p0 p1) (* 0.5 (- (cx p1) (cx p0)) (+ (cy p0) (cy p1)))) Exerccio 4.18.1 Embora correcto, o algoritmo anterior realiza trabalho desnecessrio. Isso torna-se bvio quando desenvolvemos o clculo para um polgono qualquer. Por exemplo, num polgono de trs vrtices, temos: A= 1 1 1 (x1 x0 )(y1 + y0 ) + (x2 x1 )(y2 + y1 ) + (x0 x2 )(y0 + y2 ) 2 2 2

66

ou seja 1 A = (x1 y1 + x1 y0 x0 y1 x0 y0 )+ 2 1 (x2 y2 + x2 y1 x1 y2 x1 y1 )+ 2 1 (x0 y0 + x0 y2 x2 y0 x2 y2 ) 2 evidente, na frmula anterior, que h clculos que so feitos repetidamente mas com sinais opostos. Eliminando os termos que se anulam, obtemos: 1 A = (x1 y0 x0 y1 )+ 2 1 (x2 y1 x1 y2 )+ 2 1 (x0 y2 x2 y0 ) 2 No difcil generalizar a frmula anterior para uma sequncia arbitrria de vrtices, permitindo-nos obter uma simplicao do processo de clculo: A= 1 2
n1

xi+1 yi xi yi+1
i=0

Redena a funo area-vertices de forma a tirar partido desta simplicao.

H um detalhe do algoritmo anterior que ainda no est tratado: o ltimo trapzio tem vrtices em (xn , 0), (xn , yn ), (x0 , y0 ) e (x0 , 0), mas a lista de vrtices termina em (xn , yn ). Uma forma trivial de implementarmos este detalhe consiste em acrescentarmos o vrtice (x0 , y0 ) ao m da lista de vrtices. precismente isso que a seguinte funo faz onde aproveitamos tambm para obter o valor absoluto para termos sempre uma rea positiva:
(defun area-poligono (vertices) (abs (area-vertices (append vertices (list (car vertices))))))

Finalmente, podemos aplicar este clculo a qualquer polgono fechado criado custa de linhas poligonais leves:
(defun area-polilinha-leve (entidade) (area-poligono (vertices-polilinha-leve entidade)))

Uma vez calculada a rea de um polgono arbitrrio, apenas nos falta denir o centro ( x, y ) do polgono para podermos inserir o texto que descreve essa 19 rea. O centro de um polgono pode-se denir por um processo idntico ao do clculo da rea, somando, de forma ponderada, os centros dos vrios trapzios: x i Ai x = i i Ai
19 Quando falamos de polgonos arbitrrios usual designar-se o seu centro pelo termo centride.

67

y =

i Ai iy
i Ai

Uma maneira simples de calcularmos simultaneamente x ey consiste em trat-los como coordenadas ( x, y ) e empregarmos as operaes de coordenadas xy e +xy. Para comear, precisamos de denir o centro de um trapzio de lados a e b e base d. A frmula matemtica deste centro x = d(2a + b) 3(a + b)

y =

a2 + ab + b2 3(a + b)

As frmulas anteriores so vlidas para trapzios com o canto inferior na origem e com lados a ou b no nulos. Para lidarmos com as situaes em que tal no acontece, vamos denir uma funo Lisp mais sosticada:
(defun centro-trapezio (p0 p1 / a b d) (setq a (cy p0) b (cy p1) d (- (cx p1) (cx p0))) (if (= a b 0) (xy (+ (cx p0) (/ d 2)) 0) (xy (+ (cx p0) (/ (* d (+ (* 2 a) b)) (* 3 (+ a b)))) (/ (+ (* a a) (* a b) (* b b)) (* 3 (+ a b))))))

Para calcular cada um dos produtos x i Ai e y i Ai que ocorrem como termos do numerador das fraces anteriores vamos denir a funo centro-area-trapezio:
(defun centro-area-trapezio (p0 p1 / centro area) (setq centro (centro-trapezio p0 p1) area (area-trapezio p0 p1)) (xy (* (cx centro) area) (* (cy centro) area)))

A funo anterior trata de um s trapzio. Para fazer o somatrio de todos os trapzios, denimos:
(defun centro-area-vertices (vertices) (if (null (cdr vertices)) (xy 0 0) (+c (centro-area-trapezio (car vertices) (cadr vertices)) (centro-area-vertices (cdr vertices)))))

Finalmente, para determinar o centro do polgono, temos de dividir o resultado calculado pela funo anterior pela rea total do polgono: 68

(defun centro-poligono (vertices / centro-area area) (setq vertices (append vertices (list (car vertices)))) (setq centro-area (centro-area-vertices vertices) area (area-vertices vertices)) (xy (/ (cx centro-area) area) (/ (cy centro-area) area)))

Uma vez que estamos particularmente interessados em tratar polgonos que, sabemos, o AutoCad implementa com linhas poligonais leves, vamos criar uma nova funo que, a partir de uma entidade construda com uma linha poligonal leve, calcula o seu centro a partir dos seus vrtices:
(defun centro-polilinha-leve (entidade) (centro-poligono (vertices-polilinha-leve entidade)))

Finalmente, falta apenas generalizar as funes area-entidade e centro-entidade para tratarem tambm as linhas poligonais leves:
(defun area-entidade (entidade / tipo) (setq tipo (tipo-entidade entidade)) (cond ((= tipo "CIRCLE") (area-entidade-circulo entidade)) ((= tipo "ELLIPSE") (area-entidade-elipse entidade)) ((= tipo "LWPOLYLINE") (area-polilinha-leve entidade)))) (defun centro-entidade (entidade / tipo) (setq tipo (tipo-entidade entidade)) (cond ((= tipo "CIRCLE") (obtem-propriedade entidade centro:)) ((= tipo "ELLIPSE") (obtem-propriedade entidade centro:)) ((= tipo "LWPOLYLINE") (centro-polilinha-leve entidade))))

agora possvel seleccionar entidades de tipos variados e inserir automaticamente a sua rea, tal como se apresenta na Figura 19
Exerccio 4.18.2 Um semi-octaedro uma pirmide de base quadrangular que se dene por cinco pontos: quatro para a base e um para o topo ou znite. Considere a criao de um comando AutoCad denominado semi-octaedro que pede ao utilizador as cinco coordenadas necessrias para denir um semi-octaedro e cria as linhas correspondentes s arestas do slido.

69

Figura 19: Area

70

Vous aimerez peut-être aussi