Titre : FAQ AutoLISP comp.cad.autocad Nom Archive : CAD/autolisp-faq URL : http://xarch.tu-graz.ac.at/autocad/news/faq/autolisp.html Version : 2.28 Dernière modification de la VO : datée 4 avril mais modifiée le 23 avril et publiée le 1er mai 2001 Traduction française : 4 mai 2001 Postée par : Reini Urban <rurban@x-ray.at Traduction française : Roger Rosec <cadplus@free.fr> Fréquence de postage : mensuelle Copyright : voir l'Appendice [A]
AutoLISP est un langage de script pour AutoCAD, l'ensemble de C.A.O. renommé.
Cette FAQ AutoLISP est postée dans les groupes comp.cad.autocad, alt.cad.autocad et le * .answers chaque mois.
Plusieurs FAQ AutoCAD se trouvent à
http://www.autodesk.com/support/autocad/
mais ne sont pas postées sur comp.cad.autocad.
Le contenu et les exemples s'appliquent à toutes les versions d'AutoLISP depuis la version 10, y compris
Visual Lisp, Vital Lisp et ACOMP.
Il n'y a aucun newsgroup spécial AutoLISP.
Les meilleurs sont comp.cad.autocad ou adesknews.autocad.customization. Ne vous embêtez pas à essayer comp.lang.lisp !Le code source de toutes les fonctions contenues dans cette FAQ se trouve dans FAQ-CODE.LSP - Voir l'emplacement en [A.1]
Merci à tous ceux qui ont contribué. Les corrections et contributions sont toujours bienvenues.
SVP voir http://c2.com/cgi/wiki?RulesToLiveBy pour la Nétiquette.
(| changement, + nouveau dans les repères de cet article, les commentaires intermédiaires, personnels et incertains dans <..>)
--
Les deux langages ont des avantages et des inconvénients. Vous devriez jeter un oeil sur les deux.
VB paraît être plus graphique et AutoLISP plus logique. Le concept de langage objet de VBA semble
plus facile à apprendre, mais vous ne pouvez pas lancer des commandes comme en AutoLISP.
Le nouveau VBA (dans R14.01) est extrêmement rapide. Voir aussi [5.2].
Le futur d'AutoLISP est déjà Visual Lisp.
Pour plus de détails voir
http://www.autodesk.com/vlisp,
http://www.autodesk.com/develop/devres/visual/faq.htm,
et [0.1] ci-dessous.
Le nouveau kernel (noyau) Visual LISP (anciennement Vital Lisp) remplace l'ancien kernel AutoLISP basé sur xlisp.
A quels problèmes pouvez-vous vous attendre avec Visual Lisp dans AutoCAD 2000 ? (uniquement les mauvaises nouvelles)
En http://xarch.tu-graz.ac.at/autocad/docs/r2000-news.txt
j'ai compilé un livre blanc. Utilisez LCA pour tester vos lisps.
Les points principaux sont :
Ils se sont spécialisés dans les logiciels relatifs à AutoCAD et ont une très bonne collection
AutoLISP.
D'autres sites professionnels de sharewares AutoLISP postent également leur URL dans le newsgroup.
D'importants sites portails sont nouvellement créés :
[nouveau]
TenLinks et
le E-zine (magazine électronique) UpFront qui sont les meilleurs nouveaux
services CAO.
Autodesk vient de lancer le site portail Point A.
Voici d'autres emplacements qui ont des collections AutoLISP :
Liens anciens ou rompus:
Sur le site http://support.autodesk.com/
il y avait des documents du Support Produit Autodesk qui répondent aux questions techniques. (Leur FAQ)
Le "ASA" (Support Assistance, une sorte de FAQ) était à
www.autodesk.com/support/autocad/asa2000.htm, un outil amélioré mais lent se trouve à
support.autodesk.com/KnowledgeBase.asp
Il n'y a aucune archive comp.cad.autocad ou quelque chose de rapprochant, mais il y a quelques machines de recherche qui entreposent des articles de news. En particulier :
Quelques vieux articles de news spécifiques sont aussi stockés à http://xarch.tu-graz.ac.at/autocad/news/contents.html
Jusqu'à la version 12 un SDK (kit de développement de soft) était disponible directement à AutoDESK. Les SDK1-2 ont été livrés gratuitement avec la R12 internationale. Ils incluaient beaucoup de code source et de bibliothèques pour ADS et AutoLISP.
Avec la R13 Autodesk pourvoit un réseau spécial de développeurs, comparable à celui de Microsoft, l'ADN. Le CD est comparable au vieux SDK mais est plus destiné aux développeurs ADS et ARX. .
Contactez votre revendeur Autodesk le plus proche pour en devenir un membre.
Quelques Lisps sont à l'adresse http://xarch.tu-graz.ac.at/autocad/code/adesk/SDK2/
Aux US, devenir adhérent ADN coûte 600.00 dollars par an. Les CD-ROMs n'incluent aucun SDK, mais la plupart des trucs des SDK originaux y sont inclus sous une forme ou l'autre. (Owen)
Il y a beaucoup d'autres livres AutoLISP.
Livres de LISP général (mais pas AutoLISP) qui sont à recommander :
Les fichiers Winhelp AutoLISP sont disponibles avec :
Pour DOS, il existe un programme résident en mémoire appelé
LISPHLP.EXE
(activé par Ctrl-RightShift-R, depuis 1988)
LSPDOC: En http://xarch.tu-graz.ac.at/autocad/lsp_tools/#lspdoc est disponible un outil qui crée automatiquement un Winhelp pour fichiers LISP.
La plupart des exemples qui sont publiés par les magazines ou sur plusieurs des sites du web sont très mal écrits, c'est pourquoi AutoLISP est assez dur à apprendre pour les néophytes. Le Lisp est dur à lire à cause de sa forme compacte et de ses innombrables parenthèses. Par conséquent tout le monde est tenu d'écrire le code pour qu'il soit clair et lisible. Les exemples Autodesk sont assez bien écrits mais quelquefois ils exagèrent. :-)
Il existe des petits éditeurs automatiques, appelés 'beautifiers" (embellisseurs) (voir [6.3]) lesquels formattent automatiquement votre code selon le standard.
Il y a une excellente documentation Autodesk au sujet de la programmation, les commentaires et les standards d'indentation afin de conserver le code du Lisp lisible. On la trouve sur le CD-ROM version R12 dans les SDK2 ACG.DOC ou à l'adresse http://xarch.tugraz.ac.at/autocad/docs/acg.txt
Les points les plus importants sont :
Les exemples :
;;; Fonctions de debogage :
(defun break (s)
(if *BREAK*
(progn
(princ "BREAK (stop avec )\nBREAK ")
(princ s)
(while (/= (setq s (getstring "\nBREAK ")) "")
(print (eval (read s)))
)
)
)
)
(defun dbg-print (s) ; accepte atome et listes
(if *DEBUG*
(if (listp s)
(MAPCAR 'print s)
(print s)
)
)
)
(defun C:DEBUG () (setq *DEBUG* (not *DEBUG*))) ; bascule on et off
(defun C:BREAK () (setq *BREAK* (not *BREAK*)))
(defun CONT () (setq *BREAK* nil)) ; continue sans interruption
;;; Exemple
(setq *BREAK* T *DEBUG* T)
(defun C:TEST ()
(foreach x '("1" "1.0" "1e-3")
(break "in foreach") ; stoppe l'evaluation si *BREAK* est 'on', vous pouvez entrer x
; et la valeur de x est affichee jusqu'a ce que vous entriez Enter
(setq x (atof x)) ; ceci est le code qui manipule x
(dbg-print x) ; ceci affiche la nouvelle valeur de x
; si *DEBUG* est 'on'
)
)
Command: TEST
BREAK>> (stoppez avec )
BREAK>> in foreach
BREAK>> x
1.0
BREAK>> (CONT) ; bascule 'off' l'interruption
nil
BREAK>>
1.0
1.0
0.001
Vous pouvez également basculez en 'off' la sortie de débogage avec
BREAK>> (setq *DEBUG* nil)Pour continuer en grandes boucles
Sécurité des FAS :
Sur les 2 newsgroups, il y avait récemment une longue discussion au sujet de la sécurité des FAS.
Apparamment les FAS peuvent être décompilés en code source lisible, mais cet outil n'est pas
encore disponible sur le net.La compilation FAS/VLX avec l'option Optimised/Internal est
semblable à la Kelvination.
Les noms de symboles, les chaines et nombres sont moins sécurisés que les algorithmes.
En résumé :
Un cryptage sérieux est obtenu seulement par Vital LISP et ses successeurs.
En utilisant Kelvinate, vous rendez les fichiers LISP illisibles et ils se chargent plus vite.
Les fichiers lisp protégés peuvent être décryptés facilement.
Les autres ne sont pas décryptables.
Les fichiers LISP cryptés avec Convert -e$GUARD peuvent être déprotégés
seulement avec Convert.
Il y a trois compilateurs AutoLISP, mais de meilleurs pourront être utilisés dans le futur. Certains lisps acceptent déjà, ou accepteront, ActiveX ou juste un simple FFI)
En résumé :
Les compilateurs AutoLISP sont des compilateurs du type bytecode, qui nécessitent un runtime.
AutoCAD 2000 utilise maintenant VLISP, donc le runtime y est inclus.
Vous ne pouvez pas créer de programmes autonomes, sauf avec ViLL et Vlisp où vous pouvez avoir des ARX autonomes.
Les symboles sont codés.
Il y a aussi quelques programmes AutoLISP qui comptent les parenthèses.
Externe:
interne :
Visual Lisp, Vital LISP, Emacs et Codekey fournissent des "beautifiers" internes, si je suis bien informé.
Il n'y a pas de bugs AutoLISP sérieux connus. L'interpréteur de langage lui-même
(ACADL.EXE /.EXP, VLISP) travaille bien indubitablement.
Mais il y a quelques restrictions basées sur le moteur AutoCAD, ActiveX ou Proteus (DCL).
Quelques programmes de lisp du SUPPORT sont parfois défectueux.
(exemple DDMODIFY.LSP)
Pour Visual Lisp voir le fichier README.txt qui liste tous les bugs connus et les limites. Les crashes avec les reactors sont des bugs d'acad.
Deux listes de bogues non officielles se trouvent à :
http://www.cadonline.com/exclusive/bugs/bugwatchlist.htm (la plus grande)
http://www.dotsoft.com/buglist.htm (une liste privée plus courte)
Windows: (acad_strlsort '("-1" "+1")) -> ("-1" "+1"),
DOS: (acad_strlsort '("-1" "+1")) -> ("+1" "-1")
Mais pour les 2 : (mapcar 'ascii ("-" "+")) -> (45 43)
Pour en savoir plus
http://xarch.tu-graz.ac.at/autocad/docs/strlsort-bug.html
"La fonction AutoLISP ai_propchk a été changée en c:ai_prop pour devenir similaire aux autres commandes. Cela permet d'appuyer sur Entrée pour revenir à DDMODIFY si sélectionné à partir de la barre d'outils."
Quoi d'autre ? Voir la liste de bugs AutoCAD officieuse compilée par Steve Johnson pour davantage de comportements et programmes AutoLISP défectueux à l'adresse http://www.cadonline.com/exclusive/bugs/bugwatchlist.htm
Les fichiers en LISP protégé
(equal pt1 pt2 1e-6) ; 0.000001 la tolérance de l'erreur de l'arrondi
au lieu de
(equal pt1 pt2), spécialement pour les angles.VB : Il a été reporté récemment, qu'en chargeant certains contrôles automation, la précision numériques et les particularités locales (`,´ à la place de `.´) ont des effets secondaires indésirables. Une solution et une explication de ce problème est en suspens. (Principalement pour `,´ comme délimiteur décimal dans des pays comme la France ou l'Allemagne.)
(eq "1" "1") est T alors que
dans le code compilé par ACOMP c'est nil Ce qui suit ne concerne pas des vrais bugs qui font qu'AutoLISP se plante ou retourne des résultats faux. Ce sont seulement des mauvaises mises en œuvre du langage.
AND et ORdevraient retourner la valeur de l'argument non-NIL au lieu de T. Voir http://xarch.tu-graz.ac.at/autocad/docs/and-bug.html
MAX et MIN devrait manier les types chaîne également parce que < and > accepte les chaînes.
Voir ACAD_STRLSORT ci-dessus.
Pour les Débordements de pile , voir [14] .
Vous trouverez une vue d'ensemble de tri à http://xarch.tu-graz.ac.at/autocad/lisp/#sort
En Lisp la méthode de tri principalement utilisée est le tri fusionné (utilisé aussi dans (str-sort) dans l'exemple TABLES.LSP d'AutoDESK) parce que c'est une méthode naturelle pour les listes. Normalement (en C.) vous utilisez le tri en tas (pour toutes données) ou qsort (pour les données aléatoires) et tri par insertion pour les petites listes (< 7) ou les sous-listes ans l'algorithme.
Il y a du code de lisp pour les différentes sortes de tri (shell-sort, bubble-sort, insertion-sort,
quick-sort) disponibles pour toutes données, listes de listes et indices de listes.
Dans LISP vous pouvez passer la fonction pour faire le tri, lequel est évalué en runtime,
(appelé dans ce cas method). C'est pourquoi vous n'avez besoin seulement que d'une fonction
de tri pour les types de données multiples (c.-à-d. nombres, points en x ou y, chaînes,...)
(tri donnees methode) ; method: less-than-predicate
; defaut pour les nombres: '<
Quelques exemples de durée tirés de
http://xarch.tu-graz.ac.at/autocad/lisp/sort/ur_sort.lsp :
Tri de 100 elements :
bubble sort : 13.639008 sec/ 30.08%
insertion sort: 13.368042 sec/ 29.48% (rapide pour les tris de listes)
shell sort : 13.478973 sec/ 29.73% (mise en oeuvre pauvre)
merge sort : 2.232971 sec/ 4.92%
quick sort : 2.433960 sec/ 5.37%
vl-sort : 0.099976 sec/ 0.22% (Visual/Vital Lisp interne)
acad_strlsort : 0.089996 sec/ 0.20% (AutoLISP's interne, juste les chines)
Chronométrages en cours pour vl-sort, vl-isort, STDLIB std-fast-sort, std-sort,
sts-stable-sort et le nouveau tri de Vladimir.
Ce n'est pas une question souvent posée mais elle est très intéressante parce que le Lisp lui-même est défini récursivement et c'est souvent la façon la plus facile d'articuler les problèmes graves.
Il y a quelques bons documents au sujet de la récursivité à http://xarch.tu-graz.ac.at/autocad/lisp/ et spécialement le fichier http://xarch.tu-graz.ac.at/autocad/lisp/recursive.html Tutorial par Dennis Shinn.
Il explique en grand détail :
(defun fact (n)
(cond
((zerop n) 1)
(T (* n (fact (1- n))))
)
)
Note: Il y a aussi un exemple auto-modifiable de cette fonction expliqué à http://xarch.tu-graz.ac.at/autocad/lisp/self-mod.lsp
De même qu'avec la récursivité ce n'est pas une question souvent posée, mais c'est assez
difficilement compréhensible également.
Les déclarations itératives dans AutoLISP sont : WHILE, REPEAT, FOREACH et MAPCAR.
Nous les utilisons largement dans le code de cette FAQ parce qu'ils permettent un code compact.
Il y a une courte leçon sur LAMBDA, QUOTE, MAPCAR... par Vladimir Nesterowsky :
>> "Il y a 14 trajectoires et 12 cochons.
>> Comment est-ce qu'il peut y avoir 24 canards ?"
>> Y a t-il une commande lisp qui me permettra de choisir ces
>> lignes de texte, et reconnaitre le(s) nombre(s) dans chaque ligne,
>> dans le but, disons, de multiplier chaque nombre par deux ?
>> En laissant la structure de la phrase, etc, intacte ?
Il y a un moyen. (Je suis certain qu'il y en a beaucoup d'autres)
(defun mult2 (strng) ; par Vladimir Nesterowsky
(strlgather
(mapcar
'(lambda (s / n)
(if (zerop (setq n (atof s)))
s
(rtos (* n 2))
)
)
(strlparse strng " ") ; analyse par les espaces
)
" " ; rajouter des espaces en arriere
)
)
expliqué à http://members.tri pod.com/~vnestr/mapcar.txt
;;; intervertit rangees et colonnes dans une matrice
(defun transpose(l) ; par Doug Wilson
(apply 'mapcar (cons 'list l))
)
c'est expliqué à http://xarch.tu-graz.ac.at/autocad/lisp/transpose.002.html
Les fichiers LISP peuvent être chargés au lancement d'AutoCAD en utilisant LOAD dans ACAD.LSP. Quelques LISPs qui exigent de fonctionner avec un menu adapté doivent être chargé à partir du fichier menu.MNL correspondant. Le fichier menu.MNL - si différent - devrait charger ACAD.MNL .
Les fichiers lisp qui appellent des commandes au démarrage doivent être définis dans la
fonction S::STARTUP d'ACAD.LSP. Cette fonction est appelée automatiquement au démarrage
après l'initialisation.
Autrement vous obtiendrez des erreurs
"Command list interruption (6 . 2)".
Note: (command) peut être appelé avec sûreté de l'intérieur des fichiers MNL.
(S::STARTUP) est maintenant utilisé pour appeler des menus partiels.
Si le nom du fichier ne comporte pas d'extension, la fonction LOAD ajoute .LSP.
Sans indication de chemin, le programme recherche dans le
chemin de recherche habituel d'AutoCAD qui est :
Si cependant votre programme ne se charge pas davantage, essayez peut-être de tester le chemin de recherche de votre AutoCAD.
Avec ACADLC (d'ACOMP) et la version non internationale d'AutoCAD R12 ACAD.LSP n'est pas chargé automatiquement. Par conséquent vous devez ajouter (load "ACAD" -1) dans votre ACAD.MNL.
Exemple d'ACAD.LSP :
;;;ACAD.LSP
;;; Fred the Beaver (le Castor), 12/12/94
(load "init" -1) ; cela charge quelques un de mes outils
(defun S::STARTUP ()
(load "new-end" -1) ; Redefinition de la commande FIN
)
L'argument -1 indique à la commande LOAD d'interrompre le processus de démarrage en cas d'erreur
dans le chargement d'un fichier LISP.
Si une erreur pendant le chargement survient -1 est retourné,
mais l'évaluation ne s'arrête pas. -1 peut être n'importe quelle expression.
Exemple de code qui améliore S::STARTUP dans votre code. Avec le code compilé par Visual LISP cela ne fonctionnera pas, à la place il doit être défini par DEFUN-Q. Désormais, les fonctions ne sont généralement plus des listes. Mieux que d'utiliser DEFUN-Q pour S::STARTUP, il est préférable de vérifier les appels connus, la liste des fonctions définies par l'utilisateur qui sont insérées et évaluées pendant l'exécution.
(defun MY::STARTUP () ; votre code de demarrage commence ici
;..
(princ)
)
(setq S::STARTUP
(if (and S::STARTUP (listp S::STARTUP)) ; deja defini dans ACAD.LSP ou ailleurs
(append S::STARTUP (cdr MY::STARTUP)) ; ajoute votre code
MY:STARTUP) ; juste votre code
)
ou la manière simple :
(if (and S::STARTUP (listp S::STARTUP))
(setq S::STARTUP (append S::STARTUP (list func '(princ))))
(setq S::STARTUP (list nil func '(princ))))
Voir aussi [12] Comment autocharger mes programmes ?
Comment charger mes programmes automatiquement ?
Vous pouvez aussi bien charger votre programme entier au démarrage
(Voir [11] Mon Lisp ne se charge plus au lancement)
lequel a besoin de plus de temps et de mémoire au moment du démarrage, ou bien vous pouvez
les définir par le mécanisme de l'autochargement.
Avec les programmes R14 ARX, utilisez un nouveau schéma d'autochargement
avec quelques réglages de registre et non plus par ACADRxx.LSP.
Regardez à la fin d'ACADRxx.LSP comment AutoCAD autocharge ses programmes.
;;;===== AutoLoad LISP Applications =====
...
(autoload "dline" '("dline" "dl"))
...
Ceci définit les commandes DLINE et DL dans la liste qui doit être chargée depuis le fichier DLINE.LSP quand l'utilisateur appelle les commandes DLINE ou DL pour la première fois. Avant cela, la fonction est définie simplement comme suit :
(defun C:DL () (load "DLINE")(C:DL))
En fait la définition est plus compliquée à cause de la gestion des erreurs : Ctrl-C, erreurs de chargements, mauvais fichier ou chemin.
Après le premier appel, la fonction est écrasée par la définition dans le programme.
(autoload).
(autoload) ? Avec du pur AutoLISP ce n'est pas possible.
Soit vous pouvez passer tous vos arguments dans une liste comme ceci :
;;; affiche un nombre variable d'arguments (de tout type)
(defun my-princ (x)
;; simple version, pour en savoir plus voir SDK2,: PRINTF.LLB
(if (listp x)
(mapcar 'princ x)
(princ x)
)
)
Soit vous devez définir la fonction en ADS et l'exporter dans AutoLISP. Alors vous êtes libre d'écrire :
(ads-print "Hello " "Monde " 1 2 3)ou même
(ads-printf "Hello %s %i %i" "Monde" 2 3)
Regardez les exemples ADS de Reini Urban et de Vladimir Nesterovsky à http://xarch.tu-graz.ac.at/autocad/ads/ pour les mises en œuvre des exemples ci-dessus.
Des vœux officiels ont été formulés à Autodesk concernant &optional comme renchérissement du langage AutoLISP, mais cela n'a pas été implanté pour l'instant.
Dans le vieil AutoLISP la dimension de la pile était codée hard. Elle ne pouvait pas être étendue,
mais sa dimension devait être suffisante dans la plupart des cas. Dans l'IDE (environnement de développement intégré) Visual Basic
le débordement de pile est simulé à 984 récursions ; sur la ligne de commande A2000 ou dans les programmes
chargés en dehors de l'IDE, il n'y a plus de débordement de pile. Ceci est dangereux pour vos propres
erreurs de récursivité - Voir [9]. La plupart des erreurs de débordements de pile apparaissent dans une erreur de
vos programmes et préviennent le système qu'il va tomber dans une boucle sans fin, ou si vous utilisez des fonctions
récursives sur des grandes listes. Par conséquent vous êtes limité aux listes courtes
avec les fonctions récursives et les vieilles versions.
Vous ne pouvez pas diminuer votre dimension de pile utilisée en prenant moins de paramètres locaux dans votre fonction récursive. Cependant n'utilisez pas APPLY, EVAL ou MAPCAR pour appeler votre fonction récursivement, parce qu'ils mangent la pile. Utiliser la 'queue' de la récursivité n'aide pas non plus.
Vous devez convertir votre fonction récursive en fonction itérative. (Il y a un théorème mathématique qui dit, que chaque fonction récursive peut être convertie en une fonction itérative, et même automatiquement pour les 'queues' de récursivité.) Les versions itératives peuvent utiliser des fonctions du type pile comme (push) et (pop), mais ces versions stockent la pile sur le tas (espace nodal d'AutoLisp), laquelle dimension est limitée par la quantité de votre mémoire virtuelle disponible.
Vous pouvez tester le débordement de pile avec cette simple fonction :
;;; creez une liste de nombres n (base zero)
(defun intlst (l n)
(cond ((zerop n) l)
(T (intlst (cons (1- n) l) (1- n)))
)
)
et essayez :
(setq n 100)(while (intlst nil (setq n (+ 10 n)))(print n))
Avec l'AutoLISP de la version R12/DOS vous atteignez la limite de pile avec (intlst nil 138),
avec la R13/Win il faut (intlst nil 240), avec les bi4 d'ACOMP on obtient (intlst nil 1240),
et avec Vital Lisp IDE on aura (intlst nil 984). La conversion à une version itérative produit les résultats demandés :
(defun intlst (n / l)
(repeat n
(setq l (cons (setq n (1- n)) l)) ; ceci n'est pas tres beau mais ca fonctionne
)
)
Quelques commandes ne sont pas des commandes internes à AutoCAD, ce sont de simples programmes AutoLISP commençant par C: même s'ils sont définis dans les programmes ADS. Seules les applications natives Rx (et maintenant vlax-add-cmd) exportent de vraies commandes.
Une liste de toutes ces commandes se trouve dans ACADR13.LSP dans la section AUTOLOAD
(voir aussi "[12]")
Toutes ces commandes doivent être appelées avec (C:ROTATE3D) au lieu de
(command "ROTATE3D").
Cependant les fonctions ADS peuvent prendre des arguments facultatifs.
Voir le manuel de personnalisation pour plus de détails.
c.-à-d. (c:rotate3d ss p1 p2 angle) est également valide, même (rotate3d ...)
"J'ai un problème à partir d'un fichier lisp qui doit ouvrir un dessin et continuer à tourner. Une fois que le nouveau dessin est ouvert le fichier lisp cesse d'exister dans la mémoire Autocad. Il doit être rechargé pour reconnaître les commandes."
La mémoire du Lisp est rechargée avec chaque dessin. Il y a plusieurs façons d'exécuter un Lisp sur des dessins multiples :
MYSCRIPT.SCR.
(load "monlisp")
_QSAVE _OPEN !nextdwg
(load "monlisp")
_QSAVE _OPEN !nextdwg
...
Les fonctions C: sont exportées dans AutoLISP automatiquement. Les fonctions ordinaires Vlisp/vill
doivent être exportées aussi bien avec (vl-acad-defun funcname) que par
leurs symboles qui peuvent être exportés avec un compilateur special pragma, définis dans le fichier LSP ou dans le
fichier GLD (déclarations globales). Mieux vaut utiliser des préfixes spéciaux pour de telles fonctionns.
.GLD (AUTOEXPORT-to-ACAD-PREFIX ; | prefixes de nom pour les fonctions qui doivent etre autoexportees dans AutoCAD: (chaines) |; " myx- *" )ou un par un
.LSP (pragma '((export-to-acad myx-func1 myx-func2)))Note: Il peut exister des bugs connus dans vlisp et vill avec les listes de symboles d'atomes et listes de paires pointées dans de tels arguments de fonctions et dans les valeurs de retour. (Voir "[7]")
Les fonctions exportées par des applications externes et utilisées dans votre application doivent être définies via XDF.
Les symboles (variables) dont les valeurs sont mises à jour dans Visual Lisp et dont les valeurs sont également utilisées dans AutoLISP ou AutoCAD (le menu par exemple), doivent, en premier lieu été marqués par le compilateur comme devant être externes avec:
(pragma ' ((not-localize myx:symbol)))mais la valeur doit être exportée avec
(vlisp-export-symbol 'myx:symbol)Chaque fois que la valeur est changée dans Visual Lisp, le contrôle est rendu à AutoCAD pour pouvoir obtenir la dernière valeur dans AutoLISP ou dans AutoCAD avec !myx:symbol
Dans AutoCAD 2000, c'est plus simple. Cependant vous devez alors exporter votre fonction avec VL-DOC-SET.
Voir également http://www.autodesk.com/support/techdocs/td17/td175363.htm
Pour les fonctions AutoLISP les plus générales, prière de consulter la bibliothèque "the AutoLISP Standard Library" sur le site http://xarch.tu-graz.ac.at/autocad/stdlib/ D'autres codes sont disponibles sur certains sites AutoLISP[1] ou inclus dans les SDK's par AutoDESK [1.2]
J'ai inclus ici quelques aides très utiles sur les fonctions données pour de courts exemples
dans des réponses sur le net.
Vous pouvez compter sur le fait que ces fonctions sont partie du domaine public tels que la célèbre
fonction dxf qui est définie par
(defun dxf (grp ele) (cdr (assoc grp ele))) et le compteur spécifique (getval)
qui fonctionne aussi bien avec un ename qu'avec une liste entget.
Exemples utiles de fonctions pour manipulation de liste :
;;; une liste non vide ?
(defun consp (x) (and x (listp x)))
;;; Retourne l'index du premier element dans la liste,
;;; base 0, ou nil si non trouvé
;;; (position 'x '(a b c)) -> nil, (position 'b '(a b c d)) -> 1
(defun position (x lst / ret)
(if (not (zerop (setq ret (length (member x lst)))))
(- (length lst) ret))
)
;;; Enlève un article d'une liste (les éléments en doubles sont permis)
;;; (remove 0 '(0 1 2 3 0)) -> (1 2 3)
(defun remove (ele lst) ; par Serge Volkov
(apply 'append (subst nil (list ele) (mapcar 'list lst)))
)
;;; Enlèvement conditionnel d'un article dans une liste simple
;;; fun demande exactement 1 argument
;;; (remove-if 'zerop '(0 1 2 3 0)) -> (1 2 3)
;;; (remove-if 'numberp '(0 (0 1) "")) -> ((0 1) "")
(defun remove-if (fun from)
(cond
((atom from) from) ;nil or symbol (return that)
((apply fun (list (car from))) (remove-if fun (cdr from)))
(t (cons (car from) (remove-if fun (cdr from))))
)
)
;;; Conserve tous les éléments auxquels l'attribut s'applique
;;; disons : " conserve si ", doit être défini récursivement, comme ceci.
;;; [deboggé, merci à Serge Pashkov. C'était bon dans FAQ-CODE.lsp]
(defun remove-if-not (pred lst) ; par Vladimir Nesterowsky
(apply 'append
(mapcar '(lambda(e)
(if (apply pred (list e)(list e))) lst)
)
)
;;; Ajoute un élément dans une liste s'il n'y est pas déjà
;;; astuce : accepte des listes quotées aussi, tel que
;;; (setq l '(1 2 3) (adjoin 0 'l)
;;; -> !l (0 1 2 3)
(defun adjoin (ele lst / tmp)
(if (= (type lst) 'SYM) (setq tmp lst lst (eval tmp)))
(setq lst (cond ((member ele lst) lst)
(t (cons ele lst))))
(if tmp (set tmp lst) lst)
)
;;; ROT1 - mettre le premier élément à la fin, version simple,
;;; (rotation par un)
(defun rot1 (lst) (append (cdr lst) (list (car lst))))
;;; BUTLAST - la liste sans le dernier élément
(defun butlast (lst)
(reverse (cdr (reverse lst)))
)
Quelques fonctions de chaîne utiles :
(defun stringp (s) (= (type s) 'STR))
(defun string-not-emptyp (s) (and (stringp s) (/= s "")))
;;; SYMBOL-NAME - retourne le nom d'un symbole sous forme de chaine
;;; convertit toute expression valide de lisp en representation affichable
;;; (symbol-name a) -> "a", (symbol-name '(0 1 2 a)) -> "(0 1 2 A)"
(defun symbol-name (sym / f str tmp)
(setq tmp "$sym.tmp") ; nom de fichier temporaire , devrait etre efface
(setq f (open tmp "w"))(princ sym f) (close f)
(setq f (open tmp "r") str (read-line f) f (close f))
str
)
Pour les symboles ordinaires une meilleure astuce expliquée par Christophe Candido existe à
http://xarch.tu-graz.ac.at/autocad/news/symbol-string.txt
;;; retourne la premiere valeur du groupe d'une entite.
;;; comme la fonction renommee (dxf) mais accepte tous les genres de
;;; representations de l'entite (ename, les listes entget, les listes entsel)
;;; NOTE: N'est pas utilisable pour obtenir 10 groupes dans LWPOLYLINE !
(defun GETVAL (grp ele) ; " valeur dxf" de tout entite.
(cond ((= (type ele) 'ENAME) ; ENAME
(cdr (assoc grp (entget ele))))
((not ele) nil) ; valeur vide
((not (listp ele)) nil) ; element invalide
((= (type (car ele)) 'ENAME) ; liste entsel
(cdr (assoc grp (entget (car ele)))))
(T (cdr (assoc grp ele))) ; liste entget
)
)
;;; Ex: (gettyp pline) => "POLYLINE"
(defun GETTYP (ele) ; retourne le type
(getval 0 ele)
)
;;; assurez ENAME
;;; convertit l'entite en type ENAME (pour ecrire du code plus compact)
(defun ENTITY (ele) ; convertit en nom de l'element
(cond ; accepte les types suivants :
((= (type ele) 'ENAME) ele) ; ENAME
((not (listp ele)) nil) ; erreur: aucune liste
((= (type (car ele)) 'ENAME) (car ele)) ; liste entsel
((cdr (assoc -1 ele))) ; liste entget ou nil
)
)
; et maintenant juste :
(defun getval (grp ele) (cdr (assoc grp (entget (entity ele)))))
;;; Ex: (istypep ele "TEXT")
;;; est-ce que l'element est un "SOLID" ?
(defun istypep (ele typ) ; teste le type
(= (gettyp ele) typ)
)
;;; Ex: (istypep ele '("TEXT" "ATTDEF"))
;;; is element a "TEXT" or a "ATTDEF"?
(defun ISTYPEP (ele typ) ; meilleure implementation pour accepter des listes egalement
(cond
((listp typ) (member (gettyp ele) typ)) ; fige
((stringp typ) (= (gettyp ele) typ)) ; suppose typ en majuscules, wcmatch
(T nil) ; serait bien mais plus lent
)
)
;;; Ex: (getpt (entsel)) => ( 0.1 10.0 24)
(defun GETPT (ele) ; retourne le point de depart de tout element
(getval 10 ele) ; groupe 10
)
;;; Ex: (getflag pline) => 1 si fermé
(defun GETFLAG (ele) (getval 70 ele)) ; idem avec le drapeau de l'entite;
;;; bitvalue val in flag of element set?
;;; Ex: (flagsetp 1 pline) => T si fermee
;;; Ex: (flagsetp 16 vertex) => T si un point de controle spline
(defun FLAGSETP (val ele)
(bitsetp val (getflag ele))
)
;;; Ex: (bitsetp 4 12) => T ; met la valeur du bit 4 (=2.Bit) a 12 (=4+8)
(defun BITSETP (val flag)
(= (logand val flag) val)
)
;;; Convertit le jeu de selection en liste. Lent mais facile a ecrire.
;;; Note: Il est egalement sage d'utiliser ai_ssget, parce que quelques entites
;;; pourraient être sur des calques verrouilles.
;;; Ex: (sslist (ai_ssget (ssget))) => liste d'entites selectionnees et deverrouillees
;;; ou (mapcar 'entupd (sslist (ssget "X" '((8 . "TEMP")))))
;;; - regenere toutes les entites sur le calque TEMP
(defun SSLIST (ss / n lst)
(if (= 'PICKSET (type ss))
(repeat (setq n (sslength ss))
(setq n (1- n)
lst (cons (ssname ss n) lst)
)
)
)
)
;;; appliquez une fonction a chaque entite de ss, dans un ordre renverse,
;;; Plus rapide, mais pas si facile a comprendre. Voir [22.2].
;;; [renommé de SSAPPLY en SSMAP pour se conformer au nom de stdlib]
;;; Ex: (ssmap 'entupd (ssget)) ; regenere seulement quelques entites
(defun SSMAP (fun ss / n)
(if (= 'PICKSET (type ss))
(repeat (setq n (sslength ss))
(apply fun (list (ssname ss (setq n (1- n)))))
)
)
)
Depuis la R14, utilisez INITDIA
(initdia)(command "_PLOT")
;;; [modifie pour toutes versions]
(defun DDECMD (str / tmp acadver ddestr)
(if (not (boundp 'initiate))
(cond
((= 14 (setq acadver (atoi (getvar "ACADVER"))))
(setq ddestr "AutoCAD.R14.DDE") (arxload "ddelisp"))
((= 13 acadver)
(setq ddestr "autocad.r13.dde") (xload "ddelisp"))
((= 12 acadver)
(setq ddestr "autocad.dde") (xload "ddelisp"))
(T (princ "DDE not supported")(exit))
)
)
(if (not (zerop (setq tmp (initiate ddestr "system"))))
(progn
(execute tmp (strcat "[" str "]"))
(terminate tmp)
str
)
)
)
Pour R12 utilisez "autocad.dde" comme nom de serveur. Puis à l'intérieur de votre lisp ou script, vous pouvez faire
(ddecmd "_plot ").
La fonction DDECMD retournera nil si quelque chose ne va pas, ou la chaîne que vous avez passée en cas de succès.
La chaîne est identique à ce vous écrivez au clavier en réponse aux messages de commande ;
Vous devez donc mettre un espace ou un retour chariot qui est un "^13" dans ce cas, pour terminer la chaîne.
Avec vlisp/ViLL les méthodes ActiveX peuvent être utilisées pour tracer mais la boîte de dialogue ne peut être appelée :
;;; syntaxe vlisp : (setq vlax:ActiveDocument (vla-get-ActiveDocument (vlax-get-Acad-Object))) (setq plt (vla-get-plot vlax:ActiveDocument)) ;=> objet plot (vla-PlotWindow plt pt1 pt2) ; definition de la fenetre (pts dans le SCG) (vla-PlotPreview plt 1) ; 0 pour partiel, 1 pour total (vla-PlotToDevice plt "Default System Printer") ; s'il existeAvec R14, INITDIA a été introduit et peut s'appliquer à presque toutes les boîtes de dialogues (mais pas à toutes) :
(initdia)(command "_PLOT")
Avec A2000 utilisez OLE (les méthodes VLA-) au lieu de DDE.
[21.3] (entmod) et (entmake) pour les calques, sans (command " _LAYER "...)
(setq tbl_lst (entget (tblobjname "LAYER" ""CALQUE_QUELCONQUE"))
clr_grp (assoc 62 tbl_lst)
)
(entmod (subst (cons 62 (- (cdr clr_grp))) clr_grp tbl_lst))
Vous pouvez intervertir "CALQUE_QUELCONQUE" On ou Off, même si c'est le calque courant.
(entget (tblobjname "LAYER" "Un calque quelconque")) ; R2000 accepte les espaces !
;;; Cette routine créera une calque avec n'importe quel nom vous taperez
(defun c:mlay () ; par Reinaldo Togores
(setq laynam (getstring "\nNom du calque : "))
(entmake
(list
'(0 . "LAYER")
'(5 . "28")
'(100 . "AcDbSymbolTableRecord")
'(100 . "AcDbLayerTableRecord")
(cons 2 laynam)
'(70 . 64)
'(62 . 7)
'(6 . "CONTINUOUS")
)
)
)
http://www.ez-sys.net/~coopfra/lisp.htm#view contient aussi quelques astuces.
(setvar "CVPORT" vport-id)
Voir
http://xarch.tu-graz.ac.at/autocad/news/change_vports.html
;;; Conversion de pixel en unités dessin (defun PIX->UNITS (pix) (* pix (/ (getvar "VIEWSIZE") (cadr (getvar "SCREENSIZE")))) ) ;;; Conversion d'unités dessin en pixels (defun UNITS->PIX (units) (* units (/ (cadr (getvar "SCREENSIZE"))(getvar "VIEWSIZE"))) )
Notez aussi les erreurs "Pixel Off by One" dans AutoCAD, écrit par Vibrant :
http://xarch.tu-graz.ac.at/autocad/news/pixel-off-by-one-error.txt
;;;retourne une liste des coins de la vue actuelle dans le SCG
(defun zoompts ( / ctr h screen ratio size size_2)
(setq ctr (xy-of (getvar "VIEWCTR")) ; 3D -> 2D
h (getvar "VIEWSIZE") ; reel
screen (getvar "SCREENSIZE") ; 2D: Pixel x,y
ratio (/ (float (car screen)) ; rapport aspect
(cadr screen))
size (list (* h ratio) h) ; taille de l'ecran en coordonnees
size_2 (mapcar '/ size '(2.0 2.0)))
(list (mapcar '- ctr size_2)
(mapcar '+ ctr size_2)
)
)
(defun xy-of (pt) (list (car pt)(cadr pt))) ; assure coordonnées 2D
Note: Les points retournés sont dans le système de coordonnées générales
(SCG) mais c'est correct, parce que les options "CP", "WP" et "P" de ssget attendent des points du SCG. "W" et "C"
exigent des points du SCU - Pourquoi cette différence ? va savoir !
;;; une facon de definir cette fonction
(defun ssall-visible (/ l)
(ssget "C" (car (setq l (maptrans0-1 (zoompts)))) (cadr l))
)
;;; et une autre
(defun ssall-visible-1 () ; combine "C" et (p1 p2) dans une liste
(apply 'ssget (append '("C") (maptrans0-1 (zoompts))))
)
;;; Etablit quelques points du SCG dans le SCU, plus facile avec seulement un argument
(defun maptrans0-1 (pts)(mapcar '(lambda (pt)(trans pt 0 1)) pts))
;;; CDF - la chaine est delimitee par une virgule
(defun cdf-point (pt)
(strcat (car pt) ", " (cadr pt) ", " (caddr pt))
)
;;; SDF - l'espace est le delimiteur, peut etre relu plus facilement dans AutoCAD
(defun sdf-point (pt)
(strcat (car pt) " " (cadr pt) " " (caddr pt))
)
;;; Reconvertir ces formats SDF en un point avec :
(defun str->point (s)
(eval (read (strcat "(" s ")")))
)
;;; Ecrire un fichier XYZ de tous les objets selectionnes ( voir ci-dessous SDF)
(defun C:XYZ (/ ss fname f)
(if (and (setq ss (ssget))
(setq fname (getfiled "Ecrire les coord. XYZ dans un fichier"
(strcat (getvar "DWGNAME") ".XYZ") "XYZ" 7))
(setq f (open fname "w")))
(foreach ele (sslist ss) ; -> [20.4]
(foreach pt (getpts ele) ; -> [23.1]
(write-line (cdf-point pt) f)
)
)
)
(if f (close f))
)
;;; => .xyz
;;; 0.45, 12.3, -34.0
Pour un fichier ASC (format SDF) changer simplement tous les XYZ en ASC et les points cdf en points sdf ci-dessus.
PLINE 300.2,10 350.4,10.4
;;; retourne la liste d'enget des attributs (chaine) dans l'element b (ENAME)
;;; ou nil si non trouve;
(defun ATTELE (ele attname / rslt)
(if (and (istypep ele "INSERT")
(= (getval 66 ele) 1))
(progn
(setq ele (entnext (entity ele)))
(while (and ele (istypep ele "ATTRIB"))
(if (= (strcase (getval 2 ele)) (strcase attname))
(setq rslt (entget ele) ele nil) ;break the loop
(setq ele (entnext ele))
)
)
)
)
rslt
)
;; Exemple :
(attele (entsel) "TEST") ; retourne la liste entget de
; l'attribut "TEST" si le bloc le contient.
BTW: Les mêmes fonctions plus astucieuses pour obtenir les codes DXF des entités sont
GET and EDLGETENT par Vladimir Nesterowsky.
;;; Exemples d'appels :
;;; retourne une liste des valeurs des groupes 2,1 et -1
(defun get-attribs-look-up (block-ename)
(get '(2 1 -1) (cdr (edlgetent block-ename)))
)
(defun all-vertices-and-bulges (pline-ename)
(get '(10 42) (cdr (edlgetent pline-ename)))
)
disponible à http://members.tripod.com/~vnestr/
;;; change la valeur de l'attribute en new, groupe 1
(defun ATTCHG (ele attname new / b)
(if (setq b (attele ele attname))
(entmod (subst (cons 1 new) (getval 1 b) b)))
)
;;; Changer tous les attributs du "TAMPON DATEUR" dans tous les blocs PLOT* insérés
(defun C:DATESTAMP ()
(ssmap ; debugge et renommé
'(lambda (ele)
(attchg ele "DATESTAMP" (today))
(entupd ele)
)
(ssget "X" '((0 . "INSERT")(2 . "PLOT*")))
)
)
;;; Retourne la date d'aujourd'hui (today) ; pourrait être en DIESEL ou cette conversion de chaîne
(defun TODAY (/ s)
(setq s (rtos (getvar "CDATE") 2)) ; obtient la date au format julian
(strcat (substr s 5 2) "-" (substr s 7 2)"-"(substr s 3 2))
)
Des 'tampons dateurs" automatiques sont effectués normallement aussi bien avec RTEXT}
(bonus/express tools) qu'avec un traceur HPGL/2 HPCONFIG (<a2000i).
;;; Exemple:
(setq s (getstring "Changer l'attribute en : "))
(attchg (attele (setq b (entsel "of block: ")) s)))
(entupd (car b)) ; le bloc, pas l'attribut
;;; D'autres fonctions d'aide pour obtenir l'entite principale de tout attribut
;;; ou sommet
(defun MAIN-ENTITY (ele)
(setq b (entity b)) ; force ENAME
(while (istypep b '("ATTRIB" "ATTDEF" "VERTEX"))
(setq b (entnext b))) ; boucle tant qu'il y a des sous--entites
(if (istypep b '("SEQEND" "ENDBLK"))
(getval -2 b) ; entite complexe -> en-tête (header)
b ; entite normale
)
)
Voir xarch.tu-graz.ac.at/autocad/stdlib/ENTMAKE.LSP
(setq bn (entmake '((0 . "ENDBLK")))) retourne le nom de bloc pour
(entmake (list '(0 . "INSERT")'(70 . 1)(cons 2 bn) ...)) Etant donné que LWPOLYLINE de la version R14 stocke les sommets dans une entité avec de multiples groupes 10, (assoc) ne fonctionne pas.
;;; retourne seulement quelques valeurs associées dans la liste (pour LWPOLYLINE)
(defun group-only (grp lst)
(mapcar 'cdr (remove-if-not '(lambda(pair)(= grp (car pair))) lst))
)
;;; retourne la liste des sommets d'une polyligne ou de tout autre element
(defun GETPTS (ele / pts)
(setq ele (entity ele)) ; force le type ENAME
(cond
((istypep ele "POLYLINE")
(while (istypep (setq ele (entnext ele)) "VERTEX")
;; omettre les points spline (style conservateur)
(if (not (or (flagsetp 1 ele) (flagsetp 8 ele))) ;bugfix!
(setq pts (cons (trans (getpt ele) ele 0) pts)))
(reverse pts)))
;; Cas special: vous devez la tracer, assoc trouve uniquement le premier.
;; Repare un bug LWPOLYLINE en R14: stocke en interne comme point 2D,
;; (entget) retourne des valeurs en Z fantaisistes.
((istypep ele "LWPOLYLINE")
(mapcar '(lambda(pt)(trans (list (car pt)(cadr pt) 0.0) ele 0))
(group-only 10 (entget ele))))
;; inserez ici d'autres types possibles, tels que
((istypep ele '("TEXT" "CIRCLE")) (list (getpt ele)))
;; davantage comme ceci (le style de Serge), code LWPOLYLINE ci-dessus.
(T (apply 'append (mapcar
'(lambda (n / p) (if (setq p (getval n ele)) (list p)))
'(10 11 12 13)))
)
;; ou comme ceci (style conservateur)
;;(T (foreach n '(10 11 12 13)
;; (if (setq p (getval n ele)) (setq pts (cons p pts))))
;; pts
;;)
)
)
Suggestions de Vladimir Nesteroswky pour une structure de sommet différente:
Voir [22.1]
(defun vertices-and-bulges( pline-ename )
(mapcar
'cdr
(remove-if-not '(lambda(ele) (bitsetp 9 x))
(get '(70 10 42) (cdr (edlgetent pline-ename))
=> liste de paires (10 42) de la polyligne
;;; qui peut egalement s'ecrire comme suit :
(defun flag9p (ele) (flagsetp 9 ele))) ; vrai si point spline
(mapcar
'(lambda (ele)
(remove-if-not 'flag9p (cdr (edlgetent pline-entname)))
Voir aussi [23.5] pour une forme de structure différente (segments) de polylignes. [23.2] Comment JOINDRE de multiples lignes à des polylignes ? [figé]
;;; Cet exemple convertit tous les elements selectionnes en polylignes et
;;; essaie d'en joindre le plus possible.
(defun C:JOINPOLY (/ ele ss)
(foreach ele (sslist (setq ss (ssget))) ; meilleur processus de listes
(if (entget ele) ; pas deja joint
(cond ; (alors ce serait nil)
((istypep ele '("ARC" "LINE"))
;; en fait vous devriez verifier ici les coordonnees en Z de lignes et le SCU egalement
(command "_PEDIT" ele "_y" "_j" ss "" "") ; convertit et joint
)
((and (istypep ele '("POLYLINE" "LWPOLYLINE")) ; fige
(not (flagsetp 1 ele)) ; non ferme
(< (rem (getflag ele) 128) 8)) ; ignore les mailles etc...
(command "_PEDIT" ele "_j" ss "" "") ; test SCU omis pour compacite
)
)
)
)
)
(defun C:POLYWID (/ wid ele)
(initget 5)(setq wid (getdist "Nouvelle largeur des polylignes : ")) ; non negatif
(foreach ele (sslist (ssget '((0 . "*POLYLINE")))) ;uniquement des Polylignes
(command "_PEDIT" ele "_W" wid "")
)
)
;;; Dessin d'une entite POLYLIGNE a partir d'une liste de points (idem avec SPLINE,
;;; ou LIGNE), sur le SCU courant, avec les mode d'accrochage en cours,
(defun DRAW-PLINE (pts)
(command "_PLINE")
(mapcar 'command pts)
(command "")
)
(defun DRAW-SPLINE (pts)
(command "_SPLINE")
(mapcar 'command pts)
(command "" "" "")
)
;;; Additionne la LONGUEUR de tous les objets selectionnes, discutable, vous pouvez faire
;;; de meme avec les simples AIRE en changeant la derniere ligne par (getvar "AREA")
(defun C:LEN-OF ()
(command "_AREA" "_A" "_E") ; additionne les objets (pour R12+R13)
(ssapply 'command (ssget)) ; passe tous les elements a AutoCAD
(command "" "") ; 2 fois "Entree"
(getvar "PERIMETER") ; c'est la longueur
)
;;; calcul de la longueur d'une polyligne, tout simple
(defun POLY-LENGTH (poly / seg)
(apply '+ ; la somme de toutes les longueurs des segments
(mapcar
'(lambda (seg) ; longueur d'un segment
(if (zerop (car seg))
(distance (cadr seg) (caddr seg)) ; segment de ligne ou
(abs (arclen seg seg)))) ; en courbe: -> [24]
(pline-segs poly)
)
)
)
;;; retourne tous les codes de groupe d'un élément complexe
;;; (sommets, attributs) comme liste, semblable à (edlgetent)
(defun CPLX-LIST (grp ele / lst)
(if (= 1 (getval 66 ele))
(progn (setq ele (entnext (entity ele)))
(while (and ele (not (istypep ele "SEQEND")))
(setq lst (cons (getval grp ele) lst)
ele (entnext ele)))
(reverse lst)
)
)
)
;;; PLINE-SEGS - Crée une liste de segment pour le "pname" d'une polyligne
;;; comme une liste '(courbure p1 p2). Une ligne droite a une courbure 0.0
;;; Calcule les points dans le SCU de "pname". Accepte LWPOLYLINE
(defun pline-segs (pname / pts segs)
(setq segs
(mapcar 'list
(if (istypep pname "LWPOLYLINE")
(group-only 42 (entget pname))
(cplx-list 42 pname))
(setq pts (getpts pname))
(rot1 pts))) ; ->[20.1]
(if (flagsetp 1 pname)
segs ; ferme
(butlast segs) ; ouvert: sans le dernier segment, -> [20.1]
)
)
;;; Exemple : (un peu optimise pour la compacite :)
;;; Additionne toutes les longueurs de toutes les polylignes, tranquillement,
;;; Pour accepter aussi d'autres entites, ajoutez ceci a pline-segs
(defun C:POLYLEN ()
(apply '+ (ssmap 'poly-length (ssget '((0 . "*POLYLINE"))))) ; renommée
)
Voici une version courte de stdlib :
[nouveau]
Qu'est ce que la COURBURE (BULGE ) dans un polyligne ?
La courbure est la tangente de l'angle avant inclus d'un segment courbé.
Une courbure 0.0 signifie un segment droit. Cependant avec les points de début et de fin,
il y a suffisamment d'information pour calculer rapidement toute autre dimension exigée
d'un segment en courbe.
Une courbure négative est une rotation en direction du sens horaire ("mathématiquement négatif").
longueur de l'arc = rayon*angle courbure = +-tan( ang/4 ) (CCW: +, CW -) angle = 4 * atan( courbure ) courbure = +/- ( 2*altitude ) / corde (CCW: +, CW -)
voir aussi http://www.autodesk.com/support/techdocs/fax700/lisp/fax797.htm pour un programme d'exemple du livre "Maximizing AutoLISP" [2]
(débogué, formule fausse ! Remerciements à Sergei Komarov)
;;; Convertit un segment courbe (courbure pt1 pt2) d'une polyligne
;;; en un cercle (ctr rad), les points de depart et de fin sont connus,
;;; par consequent les angles aussi : (angle ctr pt1)(angle ctr pt2)
;;; retourne nil pour un segment rectiligne !
(defun SEG2CIR (seg / bulge p1 p2 cot x y rad dummy)
(if (zerop (car seg))
nil
(setq bulge (car seg) p1 (cadr seg) p2 (caddr seg)
cot (* 0.5 (- (/ 1.0 bf) bf))
x (/ (- (+ (car p1) (car p2))
(* (- (cadr p2) (cadr p1)) cot)) 2.0)
y (/ (+ (+ (cadr p1) (cadr p2))
(* (- (car p2) (car p1)) cot)) 2.0)
rad (distance (list (car p1) (cadr p1)) (list x y))
dummy (list (list x y) rad) ; retourne ca, je deteste les progn
)
)
)
;;; Conversion inverse
;;; calcule le segment (courbure p1 p2) d'un arc
;;; avec cercle donne (ctr rad), angle de depart, angle de fin
(defun ARC2SEG (cir ang1 ang2 / p1 p2)
(setq p1 (polar (car cir) ang1 (cadr cir))
p2 (polar (car cir) ang2 (cadr cir)))
(list (arc2bul p1 p2 cir) p1 p2)
)
;;; Calcule la courbure d'un arc en ayant les points de l'arc
;;; et le cercle (ctr rad) (debogge par Serge Pashkov)
(defun arc2bul (p1 p2 cir / ang)
(setq ang (- (angle (car cir) p2) (angle (car cir) p1)))
(if (minusp ang) (setq ang (+ (* 2.0 pi) ang)))
(tan (/ ang 4.0))
)
;;; Retourne l'angle de l'arc (courbure)
(defun bul2ang (seg / ctr)
(- (angle (setq ctr (car (seg2cir seg))) (cadr seg))
(angle ctr (caddr seg))
)
)
;;; ARC2ANG
;;; calcule angle de l'arc en donnant la longueur de la corde et le rayon
(defun arc2ang (chord rad)
(* 2.0 (atan
(/ chord 2.0
(sqrt (- (expt rad 2)
(expt (/ chord 2.0) 2)
) ) ) ) )
)
;;; Longueur d'arc = rayon*angle,
;;; Note: +/-, vous aurez besoin de (abs (arclen seg)) pour la distance
(defun arclen (seg)
(* (cadr (seg2cir seg)) ; rayon
4.0 (atan (car seg)) ; angle = 4*atan(courbure)
)
)
(setq *INFINITY* 1.7e308) ; largest double
(defun tan (z / cosz) ; debogge
(if (zerop (setq cosz (cos z))) *INFINITY*
(/ (sin z) cosz)
)
)
(defun dtr (ang)(* pi (/ ang 180.0))) ; degres en radians
(defun rtd (ang)(/ (* ang 180.0) pi)) ; radians en degres
Sous Windows c'est difficile de disposer les textes à cause des polices non-monospaces.
Essayez avec les attributs de tabulations dans la disposition de la list_box tel que :
tabs = "0 20 40";
(set_tile "listbox" "Layer:\t0\twhite")
ou essayez ce qui suit pour utiliser des caractères monospaces, avec l'attribut 'fixed_width_font' :
: list_box {
label = "Drawing";
key = "dwglist";
width = 50;
fixed_width_font = true; // <- monotexte
}
Jetez aussi un coup d'oeil à la routine detab à
xarch.tu-graz.ac.at/autocad/news/detab.lsp
ou à STD-DETAB dans
xarch.tu-graz.ac.at/autocad/stdlib/STDSTR.LSP
pour convertir les tabulations en espaces.
;;; définit l'en-tête et le délimiteur de votre application (4 caractères comme nom du regapp
;;; suivant AAIG, AutoDESK Application Interoperation Guidelines)
(setq appname "HUBU-")
;;; définit * pour tous sous-types
(setq allappnames (strcat appname "*"))
;;; exemples : HUBU-LIST1, HUBU-LIST2,
;;; Voici comment obtenir la première liste EED d'un élément
(defun geteed-lst (ele)
(cdadr (assoc -3 (entget ele (list allappnames))))
)
;;; avec ceci on obtient tous les éléments de l'application typ (les métacaractères sont permis)
(defun ssget-app (typ)
(ssget "X" (list (list -3 (list typ)))
)
;;; avec ceci vous obtenez seulement vos éléments
(defun ssget-hubu (typ)
(ssget "X" (list (list -3 (list (strcat appname typ))))
)
(ssget-hubu "*") ; vous obtiendrez tous vos elements
(entget (car (entsel)) '("*"))
;;; Obtenir toutes les listes XDATA d'un element
;;; c.a.d. avec XDATA :
;;; (-3 ("HUBU-1" (1000 ."ASSHATCH")(1002 ."{")
;;; (1070 . 1)(1002 ."}")))
;;; =>(("HUBU-1" (1000 ."ASSHATCH")(1002 ."{")(1070 . 1)(1002 ."}")))
(defun getxdata (e apnlst)
(cdr (assoc -3 (entget e apnlst)))
)
;;; Toutes les listes sans le nom regapp
;;; => ((1000 ."ASSHATCH")(1002 ."{")(1070 . 1)(1002 ."}"))
(defun getxdata-all (e apnlst)
(apply 'append (mapcar 'cdr (getxdata e apnlst)))
)
Le nom du regapp est retiré ici, parce qu'il est utilisé seulement
pour un accès plus rapide à ssget. Les différentes applications sont divisées
par différents groupes (1000 . name) tels qu'utilisés par AutoDESK.
(while T ; do ; une boucle sans fin
(princ "\nTapez a=")
(setq a (getint))
(if (zerop a)(exit)) ; interromp le lisp et retourne au mode commande
)
Dans cet exemple (COMMAND) ne marche pas. (EXIT) fonctionne exactement comme Ctrl-C.
Ca affiche "error: quit / exit abort" et sort de toutes les fonctions imbriquées.
Pour fournir une interruption "silencieuse" vous devez inclure ce message d'erreur dans une fonction de
gestion des erreurs, par exemple :
(setq *olderr* *error* *error* my-error)
(defun my-error (s)
(if (not (member s ; msgs de la version anglaise :
'("Function cancelled" "console break" "quit / exit abort")))
(princ (strcat "\nError: " s))
)
(setq *error* *olderr*)
)
(defun SCRIPT-ANNULER()
(command)
(command "resume")
)
et dans un SCRIPT.SCR:
.. [] (SCRIPT-ANNULER) [d'autres commandes du script] ..
Cette FAQ est fondée sur les efforts importants de la communauté comp.cad.autocad, en particulier :
Tom Berger, Adi Buturovic, Christoph Candido, Mike Clark, Miles Constable, Cara Denko, T.J. DiTullio, Chris Ehly, Jeff Foster, Rusty Gesner, William Kiernan, Paul Kohut, Sergei M. Komarov, Joseph M. Liston, Lu, Masami Chikahiro Georg Mischler, Desi Moreno, Vladimir Nesterovsky, Roger Rosec, Serge Pashkov, Dennis Shinn, Tony Tanzillo, Eugene Tenenbaum, Reinaldo Togores, Reini Urban, Serge Volkov, Morten Warankov, Owen Wengerd, Alan Williams, Doug Wilson, Ian A. White, David Whynot, Darren Young, Xiang Zhu et beaucoup d'autres.Reini Urban