Autolisp

Bom, pela enquete, parece que lisp ainda é um assunto interessante!! Que bom, afinal isso livra a gente de muito pepino, hehehe.

O intuíto desta página será dar uma introdução ao autolisp, para que você, que ainda não sabe nada, ou muito pouco do assunto, saiba ao menos de onde começar.

Já aviso, isso não é um curso de autolisp, não é completo e nem tem a pretenção de ser. Acredito muito que a melhor maneira de aprender é o método Sr. Miagui de aprender, hehehe.

Primeiro o jeito difícil, depois o jeito fácil....

A primeira coisa que você deverá saber, é que autolisp não é uma linguagem de programação propriamente dita, na verdade é um pequeno pedaço do LISP, muito usado ainda no linux...

A sintaxe é dela é o que se chama PRÉ FIXADA. Existem três tipo de notação pré fixada, infixada e pós fixada. Mas onde se usa isso?

Exemplos:
Notação pós fixada
HP48, sem dúvida a melhor calculadora científica, financeira, programável, [adjetivos mil], que já conheci. Quem teve ou tem uma sabe do que eu estou falando.... Mesmo a HP50, a meu ver não chegou ao seus pés... Nela somamos dois números assim:

Escrevemos os argumentos, depois o operador. Aí clicamos Enter e obtemos o resultado.

Notação infixada
É como escrevemos no excel, por exemplo:


Escrevemos o primeiro operando(1), em seguida o operador (+), por fim o segundo operando (2)

Notação préfixada
É o que o autolisp usa!!! adivinha como que faz:


Colocamos em primeiro o OPERADOR (+), depois os oprandos (1 e 2)

Mas... e porque os parenteses????
Bom, isso é pra delimitar o escopo do operador.
Imagine a conta:


( * 3 ( + 1 2 ) )

Se não colocamos os parenteses, como vamos saber o que soma e o que multiplica?
Claro existe uma definição melhor pra isso. Procura na wikipédia...
 Note que se escrevêssemos no excel, faríamos:

fx = 3 * ( 1 + 2 )

 E ainda, a sintaxe do autolisp permite fazer isso:


( + 1 2 3 4 )

Economizando parenteses....

Bom, agora que já sabemos a estrutura básica da sintaxe, o que mais precisamos saber???

Primeiro, que é aconselhável usar uma IDE. O AutoCAD possui sua própria IDE para escrever programas em autolisp, é o VLIDE.

Então digite VLIDE na linha de comando, pressione ENTER e veva a tela que se abre:


Note é que apenas um editor de texto metido, hehehe

Você notou que o texto é colorido? Esse é uma feature de todos os IDEs. Eles colorem o texto pelo tipo de objeto. no caso do lisp, temos:



As cores ajudam na visualização do código.

E agora? Agora, você deveria saber formular algorítimos para resolver seus problemas...
Mas como assim?

É o seguinte, escrever porgramas em autolisp, C, C++, VB, java é simples, é so uma questão de sintaxe... o difícil é formular a idéia do algorítimo.


Por exemplo. Precisamos de um programa que numere os pontos de uma poligonal. Simples, não? existem milhares de soluções pra isso já, na internet.... Mas, pense, o que será preciso para escrever este programa?

Veja:

  1. Um nome, para chamarmos ele
  2. Depois de chamarmos, ele deverá pedir a seleção de uma polilinha.
  3. Se temos a polilinha, vamos numerar!! 
  4. Começa com que número?
  5. Temos que obter os vértices da polilinha.
  6. Iterar sobre cada um deles, escrevendo o texto.

Parece simples não? E é!!! Este é o nosso algorítimo para este caso.

E se quisermos somar o comprimento de linhas selecionadas?, novamente, precisamos definir o algorítimo:

  1. Nome para chamarmos o comando
  2. Pedir a seleção de linhas
  3. Iterar em cada linha, calculando seu comprimento e somando com o total anterior
  4. devolver o resultado

Agora, como fazemos cada passo desses? Já dizia, jack, vamos por parte, hehehe

Já definimos o algorítimo, então já temos o problema. Dividimos cada parte dele num problema menor. Agora, vamos resolver um por um.

No primeiro programa, precisamos saber como criar um comando que podemos chamar na linha de comando. Em visual lisp (é o autolisp colorido do VLIDE), fazemos asim:



(defun c:nome_do_comando variaveis locais )
 
;faz alguma coisa legal
)



Note os parentese em vermelho, a função defun em azul, o nome do comando em preto
 Agora, escreva isso no VLIDE e salve com o nome que você quiser:



Já resolvemos o primeiro problema. Agora, como pedimos a seleção de uma polilinha?
Aqui vamos fazer uma pausa, porque eu não vou explicar todas as funções do visual lisp. Então sugiro você dar uma olhada no help .


No geral, as funções que pedem alguma coisa na linha de comando do AutoCAD, começam com GET. Então vamos olhar as funções que começam com G!! Se não achar nada, podemos procurar por entity. No visual lisp, as coisas que estão desenhadas na tela do cad são entities (entidades, ou objetos).

Eu encontrei esta: ENTSEL


Vamos ver sua sintaxe:
( entsel [msg] )


E como usamos isso? Confesso que quando comecei a aprender a programar, eu olhava para as funções e nem desconfiava o que era aquilo entre colchetes, hehehehe


Quando algo está entre colchetes no help do visual lisp, é porque é um argumento (ou operando) que é opcional. Neste caso colocar uma [msg] é opcional.


Se fizermos isto na linha de comando do dad:





Percebeu? Ele colocou o texto "Select Object". Mas e se colocarmos outro text, por exemplo:





Ficou bacana, né? E se clicamos a polilinha, será escrito na linha de comando:
Command: ( entsel "\nSelecione a polilinha:" )
Selecione a polilinha:(<Entityname 7fffb5b65a0> (1818.91 577.047 0.0))



Mas o que é isso? Olhando no help (!!!!), são dois valores.

O primeiro < entity 7fffb5b65a0="" name:="" > é um ponteiro para a polilinha ( em C, ponteiro um objeto que nos dá acesso a algo maior, sem blá, blá, blá ). É como o seu CPF, ou RG, ou C.U. (este é um blog de respeito!!).

O segundo valor nada mais é que as coordenadas do cursos na hora do clique (Hum, isso poderá ser útil!!).

Já sabemos como selecionar a polilinha. Precisamos salvar isso numa varivel para usar depois. Mas como salvamos alguma coisa numa variavel em autolisp??

Antes, queriamos pegar (get) alguma coisa. Agora queremos salvar, setar, definir como (set), uma variável. Então no help, encontramos SETQ .


Sua sintaxe é:

setq variavel entsel "\nSelecione a polilinha" ) )


Onde 'variável' será quem armazena alguma coisa e 'valor' é ess alguma coisa. Juntando as duas funções, teremos:

( setq variavel ( entsel "\nSelecione a polilinha" ) )

Já pedimos a seleção, já definimos o comando. Vamos juntar tudo!!




Percebe?. Agora salve. Na linha de comando do cad, digite APPLOAD. Se você ainda não sabe, este é o comando que carrega a nossa lisp para podermos usar como se fosse um comando


Só que aí em umas pegadinhas. O comando só pode ser usado se definimos ele com a função defun e se colocamos o prefixo C: no seu nome.

Olhe novamente para o código. Viu? ( defun c:... 

sem o defun, o autolisp pensará que ( nome_do_comando ) é uma função definida pelo usuário. Em autolisp chamamos isso de subrorina (eu pelo menos... ) e esta está sendo CHAMADA e não DEFINIDA.

E se esquecemos do C:, a subrotina não é acessivel na linha de comando como se fosse um comando. a unica forma de chamar seria assim:

( nome_da_subrotina )


Bom, se você já carregou, digite nome_do_comando, na linha de comando!!




Chique, não??

Resolvemos dois problemas!! Agora, precisamos iterar sobre os vértices dessa polilinha.

Iterações em algorítimos se fazem com FOR, DO, WHILE... REPEAT...

Todas as liguagens de programação tem uma estrutura de looping. o Lisp não é diferente. Nele, temos o FOREACH, o WHILE e o REPEAT.

Para iterar, normalmente tambem precisamos de um contador, que é incrementado a cada looping da iteração.

Já temos o ponteiro para  a polilinha ( Entity Name.... ) Mas precisamos do objeto em si, com suas propriedades. No AutoCAD cada objeto é armazenado como uma lista de propriedades, e estas propriedades são classificadas segundo seu significado.


Por exemplo:



((
-1 

< Entity name: 7fffb5b65c0 >
)  (


"LINE"

;tipo de objeto
(
330 
.       
< entity fffb5a19f0 >
)  (


"B3C"

;handle
(
100 

"AcDbEntity"
)  (
67 

0
)  (
410 

"Model"
)  (


"0"

;layer
(
100 

"AcDbLine"
)  (
10 
1840.14 725.607 0.0

;ponto inicial
(
11 
1931.3 769.302 0.0

;ponto final
(
210 
0.0 0.0 1.0
))

Esta é a lista de uma LINE. Para obter a lista de um objeto qualquer, digite, na linha de comando:


(entget ( car (entsel ) ) )


Não se preocupe em não entender a inha acima agora. é so pra testar.

Olhando para a lista, vemos que temos uma lista organizada, onde cada item é uma outra lista, que contem 3 ou mais coisas.

Basicamente cada item é um objeto que possui um identificador e um valor. o Ponto serve para delimitar onde termina o identificador e onde começa o valor.

Assim:
 0 (zero) serve para dizer qual o tipo do objeto, que pode ser "LINE", "LWPOLYLINE", etc
8, é o layer do objeto
10, é a coordenada inicial (no caso de lines)
11, é a coorenada final (no caso de lines)
410, diz se está no model ou paper (na verdade em qual espaço se encontra o objeto)


Para polilinhas, temos,

IdentificadorValorDescrição
0"LWPOLYLINE"Tipo de objeto
8"0"layer da polilinha
905Número de vértices
101 2Coordenadas X e Y em OCS do vértice. tem um desses para cada vértice

Claro que tem mais um monte de outros. Veja quais são em DXF ENTITIES

Então é só escolher os identificadores que começam com 10 para obter as coordenadas, correto? Sim Daniel Sam!!!

Lembra que eu disse que tem o FOREACH? Então. Sua sintaxe é:

(FOREACH var lista 
 ;faz algo legal
)

Ele faz um looping, atribuindo o valor do item Nésimo à variavel 'var' e executa alguma coisa com esta variável.

Volte no seu programa.

Pegamos a polilinha e armazenamos seu ponteiro na variável chamada 'variavel'

Mas esta variavel também armazena o ponto clicado. No momento ele não nos interessa. Vamos pegar somente o promeiro elemento que é o ponteiro. Pra pegar o primeiro elemento de uma lista, podemos usar a função:

( NTH N lista )

(Ah, resolvemos mais um pequeno problema. Como pegar o Nésimo item de uma lista!!!)

E como fazemos para obter a LISTA????

Uma pausa!!



o Ponteiro ( Entiny Name.... ) chamamos de ENAME.
a lista que o ENAME aponta é o ELIST




Para obter o ELIST, usamos a função

( ENTGET ename ) 

 Juntando então estes pedaços, temos:




(defun c:nome_do_comando variavel ename elist )
  
;pede a selecao da polilinha
  (setq variavel (entsel "\nSelecione a polilinha"))
  
;pega o ename da polilinha
  (setq ename nth variavel ) )
  
;pega o elist da polilinha
  (setq elist (entget ename ) )
  
;itera sobre os item da elist
  (foreach obj elist
    
;faz algo legal 
    )
)




Agora, falta sabermos como desenhar um texto, como verificar se o item OBJ é uma coordemada ou outra coisa e outros probleminhas menores!!

Lembra que eu falei sobre subrotinas?
Bom, podemos usar uma que eu ja fiz, é a draw-text.

Sua sintaxe é assim:
(draw-text str pt lay rot alt st ali )

Onde:
str, STRING é o texto que queremos escrever
pt, ( X Y ), coordenadas
lay, STRING, nome do layer que o texto ficará
rot, REAL, rotação em radianos em relação ao WCS
alt, REAL, altura do texto
st, STRING, é o textstyle
ali, STRING, mc, bc, l, ml.... alinhamento do texto

ou se você preferir, poderá simplesmente fazer:

( command "._text" pt alt rot str "" )

Eu particularmente não gosto do command... Você até pode achar mais simples, mas quando for usar DCL, talvez vocÊ entenda porque não usar ele.....


De qualquer forma, resolvemos mais um pedaço do problema. Vou supor que você escreveu a segunda opção, usando o command. Mas para que você entenda a idéia de subrotina, vamos escrever ele como uma subrotina, assim:



;subrotina que desenha textos
(defun draw-text (str pt lay rot alt st ali)
  (
command
    
"._text" "J" ali ;alinhamento do text
    pt               ;ponto de iserção do texto
    alt              ;altura do texto
    rot              ;rotação
    str              ;o texto a ser escrito
    )
  )



Esta subrotina, irá funcionar?
Talvez. Se o OSNAP estiver ligado, pode ser que ele atrapalhe!!
Se o estilo de texto não existir, dá erro!!
Se o estilo de texto existir e tiver altura de texto diferente de zero, dá erro!!!
Então esses são os motivos pelos quais eu não gosto do COMMAND ...
A principio, deixe assim mesmo. Depois melhoramos o nosso programa.

Lembra do FOREACH ? e o ELIST?

no ELIST, precisamos somente daqueles items que se identificam com o ID=10

então, precisamos testar, se o identificador é realmente o 10.
Pra isso, podemos adicionar um IF!!!

Qual a sintaxe do IF? O help, diz:

(if teste
    ( then_alguma_coisa ) ; se teste é diferente de NIL
    ; [ (else_outra_coisa ) ] ; se teste é nil
)

Em lisp, tudo menos o NIL é verdadeiro.

Vamos, lá, fica assim:


Note que adicionei algumas funções que não comentei. São elas: CAR, CDR.Nada de mais.

Agora vamos testar!! Salve a lisp, Carregue com APPLOAD

O que faltou???
Incrementar o contador, claro!!!!

Vamos fazer uma pequena alteração no programa. Localize a linha depois do If:



Escreva :
 (setq cont (1+ cont ) ) 
Nesta linha

Peraí, eu posso escrever função dentro de outra função?

Pode, mas tome cuidado. O lisp sempre devolve o último argumento avaliado (putz, nem eu entendi isso... )

É assim, se um bloo e parenteses faz uma porção de coisas, por exemplo:

( setq a 1 b ( + a 1 ) )

Isso retorna 2. Pois 1 é armazenado em 'a', depois a é somado a 1, o resultado é armazanado em 'b'
O armazenamento em 'b' 'r a última coisa avaliada, efetuada, calculada, no bloco do setq. então isso é retornado....

Então voltemos ao código....

Salve. Carregue com APPLOAD. Teste!!!

Deu certo??

Então use o UNDO para desfazer....
O que acontece? Precisa fazer UNDO tantas vezes quanto o número de vértices, certo?

Isso não é o comportamento mais desejado. Seria interessante que com apenas UM UNDO, desfizesse tudo.

Este é outro problema, correto?

Outra coisa, clique F2. Olhe a linha de comando.... tosco né? Precisamos que não escreva nada alem do necessário!!! Isso é ainda mais interessante qnado compilamos a nossa lisp para VLX... para proteger o código.

Pior ainda, ligue o OSNAP e deixe o INSERT ligado...

Pode ser que os textos se sobreponham todos na mesma coordenada!!!! pois o comando texto pode achar o INSERT do primeiro ou de um texto qualquer!!!

Não vou colar o código todo aqui. Quero que você o digite!!! Miaghi, lembra???

Gostou? Compartilha!!!

Na próxima, eu digo como resolver esses probleminhas acima!!!


Um abraço ao Luis Silva, que havia salvo as imagens em um documento do WORD para consulta e me possibilitou arrumar o tutorial!!

11 comentários:

  1. caramba !!!! excelente tutorial !!

    eu estava essa semana procurando sobre Autolisp, mas o pouco conteúdo que existe parece que foi feito pra pessoa desistir de aprender heheh

    Vou até imprimir pra mostrar pro pessoal do trabalho.

    enfim, muito obrigado!

    ResponderExcluir
  2. Bom dia!

    apocax, desculpe mas vou ter que discordar com você um pouco, tem muito material sobre lisp, o problema é que diferente do aprendizado de outras linguagens de programação, é dificl encontrar um material objetivo, ou que faça entender o uso correto da linguagem.

    Tenho bastante material que consegui pedindo a esses grandes mestres e muitos baixados da net, porém poucos realmente ensinam, mostram mais o que faz cada função, mas não como usá-la dentro da rotina.

    Tenho essa dificuldade em correlacionar as funções dentro da rotina pra obter o que quero e esse artigo foi com certeza um pulo do gato no sentido de orientar o que se deve fazer.

    Newton, já pensou em fazer um livro ou dar curso a distância?
    Sua didática é ótima.

    ResponderExcluir
  3. Pois é, antigamente havia o www.autolisp.com.br, que nao existe mais... lá tinha tudo

    acredito que o problema stá em formular o algoritimo, a receita do bolo... nao na linguagem usada. perceba que o exemplo que usei é bastante simples depois de escrito, mas até você pensar em como o programa deve fazer perguntas e como ele deve agir, é bastante complicado.

    feito o algorítimo, o resto é sintaxe

    quanto ao livro, ja pensei sim, mas nao tenho tempo pra isso ainda....

    ResponderExcluir
  4. Newton,

    Seu post me incentivou, comecei a estudar mais e por isso já tõ aqui pra tentar tirar dúvidas.

    Eu escrivi esse script para criação de tubulação unifilar para uso na diciplina de hidrossanitário onde o texto do tubo já é colocado automaticamente:

    ; Diâmetro de Ø20mm
    (DEFUN C:tu20 (/ ponto1 ponto2 )
    (setq ponto1 (getpoint "\n>Primeiro ponto<\n"))
    (setq ponto2 (getpoint "\n>segundo ponto<\n"))
    (command "_line" ponto1 ponto2 "")
    (Command "TEXT" "j" "bc" "m2p" ponto1 ponto2 ".15" ponto2 "Ø20mm")
    )

    alguns problemas que verifiquei para fazer melhorias são:
    Quando pede pra inserir a linha o texto "primeiro ponto" e "segundo ponto" ficam que meio subtendido, e quando a linha é traçada não visualizo a mesma pra caso eu ative o orto ou queira ver a direção que está indo.

    Outro problema é o fato é o de não poder ir continuando a traçar a linha repetindo a inserção do texto sempre tendo que clicar em dois pontos para inserção da linha e texto

    Se puder mandar alguma dica pra resolver isso, eu penso em fazer varias outras melhorias e já estou estudando aqui, como deixar o tubo no layer, o texto também em seu layer, criar um menu pra em um comando unico poder escolher qual tubo vou querer.

    Agradeço!

    ResponderExcluir
  5. Fiz novas alterações, dessa vez inserindo o joelho:

    ; nova revisão
    (DEFUN C:tubo (/ ponto1 ponto2 )

    ;CRIANDO LINHA DO TUBO
    (setq ponto1 (getpoint "\n>Primeiro ponto<\n"))
    (setq ponto2 (getpoint "\n>segundo ponto<\n"))
    (command "_line" ponto1 ponto2 "")

    ;CRIANDO JOELHO
    (setvar "cmdecho" 0)
    (setq old-osmode (getvar "osmode"));;;;memoriza o osnap atual
    (setvar "osmode" 0);;;;;aqui vc desabilita o osnap


    ;CRIAR A PRIMEIRA LINHA EM RELAÇÃO AO PONTO 1
    (command "_ucs" "e" "last")
    (command "_line" "from" "0,0" "@0,.075" "@0,-.15" "")
    (command "_move" "last" "" "0,0" ".15,0" "")
    (command "_ucs" "" "")

    ;CRIAR A SEGUNDA LINHA EM RELAÇÃO AO PONTO 2
    (command "_copy" "last" "" ponto1 ponto2 "")
    (command "_ucs" "e" "last")
    (command "_move" "last" "" "0,0" "0,-.30" "")
    (command "_ucs" "" "")



    ;CRIANDO TEXTO DO TUBO
    (Command "TEXT" "j" "bc" "m2p" ponto1 ponto2 ".15" ponto2 "Ø20mm")

    (setvar "osmode" old-osmode);;;;retorna o osnap atual
    (setvar "cmdecho" 1)
    (princ)


    ); nova revisão
    (DEFUN C:tubo (/ ponto1 ponto2 )

    ;CRIANDO LINHA DO TUBO
    (setq ponto1 (getpoint "\n>Primeiro ponto<\n"))
    (setq ponto2 (getpoint "\n>segundo ponto<\n"))
    (command "_line" ponto1 ponto2 "")

    ;CRIANDO JOELHO
    (setvar "cmdecho" 0)
    (setq old-osmode (getvar "osmode"));;;;memoriza o osnap atual
    (setvar "osmode" 0);;;;;aqui vc desabilita o osnap


    ;CRIAR A PRIMEIRA LINHA EM RELAÇÃO AO PONTO 1
    (command "_ucs" "e" "last")
    (command "_line" "from" "0,0" "@0,.075" "@0,-.15" "")
    (command "_move" "last" "" "0,0" ".15,0" "")
    (command "_ucs" "" "")

    ;CRIAR A SEGUNDA LINHA EM RELAÇÃO AO PONTO 2
    (command "_copy" "last" "" ponto1 ponto2 "")
    (command "_ucs" "e" "last")
    (command "_move" "last" "" "0,0" "0,-.30" "")
    (command "_ucs" "" "")



    ;CRIANDO TEXTO DO TUBO
    (Command "TEXT" "j" "bc" "m2p" ponto1 ponto2 ".15" ponto2 "Ø20mm")

    (setvar "osmode" old-osmode);;;;retorna o osnap atual
    (setvar "cmdecho" 1)
    (princ)

    )

    ResponderExcluir
  6. coloque um looping tipo:

    ( while
    ( setq p1 (getpoint "p1:")
    p2 (if p1 (getpoint p1 "p2:")))
    ;...
    )

    ResponderExcluir
  7. Newton funcionou até certo ponto, pois a colocação de joelhos ficou toda doida:

    (DEFUN C:tubo (/ ponto1 ponto2 )

    ;Selecionando os pontos
    (while
    (setq ponto1 (getpoint "\n\tSelecione o ponto ou ENTER para sair: ")
    ponto2 (getpoint ponto1 "\n\tSelecione o ponto ou ENTER para sair: "))

    ;Desabilitando Osnap
    (setvar "cmdecho" 0)
    (setq old-osmode (getvar "osmode"));;;;memoriza o osnap atual
    (setvar "osmode" 0);;;;;aqui vc desabilita o osnap


    ;CRIANDO JOELHO

    ;CRIAR A PRIMEIRA LINHA EM RELAÇÃO AO PONTO 1
    (command "_ucs" "e" "last")
    (command "_line" "from" "0,0" "@0,.075" "@0,-.15" "")
    (command "_move" "last" "" "0,0" ".15,0" "")
    (command "_ucs" "" "")

    ;CRIAR A SEGUNDA LINHA EM RELAÇÃO AO PONTO 2
    (command "_copy" "last" "" ponto1 ponto2 "")
    (command "_ucs" "e" "last")
    (command "_move" "last" "" "0,0" "0,-.30" "")
    (command "_ucs" "" "")

    ;Calculando Distâcia
    (setq dist (distance ponto1 ponto2))
    (setq dist1 (RTOS dist 2 2)) ;COnvert dist1 para string pegando apenas 02 casas decimais

    ;Texto informando distância
    (Command "TEXT" "j" "tc" "m2p" ponto1 ponto2 ".15" ponto2 dist1)

    ;Texto informando diâmetro
    (Command "TEXT" "j" "bc" "m2p" ponto1 ponto2 ".15" ponto2 "Ø20mm")

    ;Retornando Osnap
    (setvar "osmode" old-osmode);retorna o osnap atual
    (setvar "cmdecho" 1)

    ;Desenhando o tubo
    (command "_line" ponto1 ponto2 ""))

    (princ)

    )

    ResponderExcluir
  8. cara, vc poderia arrumar as figuras da pagina? porque elas não estão carregando, valeu 1 abr

    ResponderExcluir
  9. Alguém tem o arquivo dessa lisp pronta ?

    ResponderExcluir
  10. Anônimo5/3/21 18:32

    Boa noite! Estou com dificuldades em definir um filtro no código abaixo... Vamos ver se vc pode me ajudar:

    (setq nonoss
    (ssget
    (list
    (cons 0 "insert")
    (cons -4 "")
    )
    )
    )

    ResponderExcluir