AutoLISP : le guide complet — Chapitre 14 / 16

Données étendues et dictionnaires

Jusqu'ici, nous avons créé, modifié et lu des entités avec leurs propriétés géométriques standard : points, rayons, calques, couleurs. Mais dans un programme réel, vous aurez souvent besoin d'attacher vos propres informations à une entité — un numéro de pièce, une référence fournisseur, un lien vers une autre entité. AutoLISP offre plusieurs mécanismes pour cela, du plus simple au plus puissant.

Les données étendues (xdata)

Les données étendues (extended data ou xdata) permettent d'attacher des informations personnalisées à n'importe quelle entité du dessin. Ces données sont invisibles pour l'utilisateur — elles ne s'affichent pas à l'écran — mais votre programme peut les lire et les modifier.

Enregistrer une application

Avant d'attacher des xdata, vous devez enregistrer un nom d'application avec regapp. Ce nom sert d'identifiant pour regrouper vos données :

(regapp "WIIP_TUYAUTERIE")

regapp retourne le nom de l'application si l'enregistrement réussit, ou nil si le nom est déjà enregistré (ce qui n'est pas une erreur). En pratique, on appelle regapp au début du programme sans se soucier de la valeur de retour.

Convention : choisissez un nom d'application unique qui identifie clairement votre programme. Un préfixe avec le nom de votre société évite les collisions avec d'autres développeurs.

Attacher des xdata à une entité

Les xdata sont ajoutées au groupe -3 de la liste DXF d'une entité. Chaque donnée a un code DXF spécifique :

Code Type de donnée Exemple
1000 Chaîne de caractères (1000 . "DN100")
1001 Nom d'application (début d'un groupe xdata) (1001 . "WIIP_TUYAUTERIE")
1005 Référence de maintien (lien vers une autre entité) (1005 . "1A3F")
1010 Point 3D (1010 0.0 0.0 0.0)
1040 Nombre réel (1040 . 114.3)
1070 Entier 16 bits, de -32 768 à 32 767 — typique pour les index de couleur, les flags (1070 . 42)
1071 Entier long 32 bits, de -2 147 483 648 à 2 147 483 647 — pour les grandes valeurs (1071 . 100000)

Voici comment attacher des xdata à la dernière entité créée :

;; Enregistrer l'application
(regapp "WIIP_TUYAUTERIE")

;; Récupérer la dernière entité
(setq entite (entlast))
(setq donnees (entget entite))

;; Ajouter les xdata
(setq donnees
  (append donnees
    (list
      (list -3
        (list "WIIP_TUYAUTERIE"
          (cons 1000 "DN100")           ; Diamètre nominal
          (cons 1040 114.3)             ; Diamètre extérieur en mm
          (cons 1070 10)                ; Pression nominale (PN)
        )
      )
    )
  )
)

;; Appliquer la modification
(entmod donnees)

Le code -3 signale à AutoCAD que ce qui suit est un bloc de données étendues. À l'intérieur, les données sont groupées par nom d'application.

Lire les xdata d'une entité

Par défaut, entget ne retourne pas les xdata. Il faut explicitement demander les données d'une application :

;; Lire l'entité AVEC ses xdata
(setq donnees (entget (entlast) '("WIIP_TUYAUTERIE")))

;; Extraire le groupe xdata
(setq xdata (assoc -3 donnees))

Le résultat de (assoc -3 donnees) ressemble à :

(-3 ("WIIP_TUYAUTERIE"
      (1000 . "DN100")
      (1040 . 114.3)
      (1070 . 10)
    )
)

Pour extraire une valeur précise :

;; Récupérer les données de l'application
(setq donnees-appli (cdadr (assoc -3 donnees)))

;; Chercher le diamètre nominal
(cdr (assoc 1000 donnees-appli))  ; → "DN100"

;; Chercher le diamètre extérieur
(cdr (assoc 1040 donnees-appli))  ; → 114.3

Exemple complet : étiqueter des entités

Ce programme attache un identifiant unique à chaque entité sélectionnée, puis permet de le retrouver :

(defun c:etiqueter (/ jeu index entite donnees identifiant)
  (regapp "WIIP_ID")
  (setq jeu (ssget))

  (if jeu
    (progn
      (setq index 0)
      (repeat (sslength jeu)
        (setq entite (ssname jeu index))
        (setq identifiant (strcat "ID-" (itoa (1+ index))))

        ;; Lire l'entité
        (setq donnees (entget entite))

        ;; Ajouter les xdata
        (setq donnees
          (append donnees
            (list
              (list -3
                (list "WIIP_ID"
                  (cons 1000 identifiant)
                )
              )
            )
          )
        )

        (entmod donnees)
        (setq index (1+ index))
      )

      (princ (strcat "\n" (itoa (sslength jeu)) " entité(s) étiquetée(s)."))
    )
    (princ "\nAucune sélection.")
  )
  (princ)
)

(defun c:lire-etiquette (/ selection entite donnees xdata)
  (setq selection (entsel "\nSélectionnez une entité : "))
  (if selection
    (progn
      (setq entite (car selection))
      (setq donnees (entget entite '("WIIP_ID")))
      (setq xdata (assoc -3 donnees))

      (if xdata
        (princ (strcat "\nIdentifiant : " (cdr (assoc 1000 (cdadr xdata)))))
        (princ "\nCette entité n'a pas d'identifiant.")
      )
    )
    (princ "\nAucune sélection.")
  )
  (princ)
)

Les dictionnaires d'extension

Les xdata sont parfaites pour attacher des données à une entité spécifique. Mais que faire si vous voulez stocker des informations au niveau du dessin lui-même — par exemple, la configuration de votre programme, un compteur global, ou une table de correspondance ? C'est le rôle des dictionnaires d'extension.

Le dictionnaire des objets nommés

Chaque dessin AutoCAD contient un dictionnaire des objets nommés (Named Object Dictionary ou NOD). C'est la racine de l'arborescence des dictionnaires. Vous y accédez avec la fonction namedobjdict :

(setq dictionnaire-racine (namedobjdict))

Cette fonction retourne le nom d'entité du dictionnaire racine. Vous pouvez ensuite y chercher ou y ajouter des entrées.

Chercher dans un dictionnaire

La fonction dictsearch cherche une entrée dans un dictionnaire par son nom :

;; Chercher un dictionnaire nommé "WIIP_CONFIG"
(setq resultat (dictsearch (namedobjdict) "WIIP_CONFIG"))

Si l'entrée existe, dictsearch retourne sa liste DXF. Sinon, elle retourne nil.

Stocker des données avec des xrecords

Un xrecord est un objet de la base de données qui peut contenir des données arbitraires. C'est l'équivalent d'un enregistrement dans une base de données. On le crée avec entmakex (variante d'entmake qui retourne le nom d'entité) :

;; Créer un xrecord
(setq xrecord
  (entmakex
    (list
      '(0 . "XRECORD")
      '(100 . "AcDbXrecord")
      (cons 1 "Tuyauterie industrielle")   ; Chaîne
      (cons 70 42)                          ; Entier
      (cons 40 3.14)                        ; Réel
    )
  )
)

Les codes DXF standard sont utilisés pour typer les données dans un xrecord :

Code Type
1 Chaîne de caractères
10 Point 3D
40 Nombre réel
70 Entier
300-309 Chaînes supplémentaires

Ajouter un xrecord au dictionnaire

Une fois le xrecord créé, il faut l'attacher au dictionnaire des objets nommés avec dictadd :

(dictadd (namedobjdict) "WIIP_CONFIG" xrecord)

Exemple complet : configuration persistante

Ce programme stocke et lit une configuration dans le dessin :

(defun sauvegarder-configuration (nom-projet echelle / xrecord existant)
  ;; Vérifier si la configuration existe déjà
  (setq existant (dictsearch (namedobjdict) "WIIP_CONFIG"))

  (if existant
    ;; Supprimer l'ancien xrecord
    (dictremove (namedobjdict) "WIIP_CONFIG")
  )

  ;; Créer un nouveau xrecord
  (setq xrecord
    (entmakex
      (list
        '(0 . "XRECORD")
        '(100 . "AcDbXrecord")
        (cons 1 nom-projet)
        (cons 40 echelle)
      )
    )
  )

  ;; L'ajouter au dictionnaire
  (dictadd (namedobjdict) "WIIP_CONFIG" xrecord)
  (princ (strcat "\nConfiguration sauvegardée pour " nom-projet))
  (princ)
)

(defun lire-configuration (/ resultat)
  (setq resultat (dictsearch (namedobjdict) "WIIP_CONFIG"))

  (if resultat
    (progn
      (princ (strcat "\nProjet : " (cdr (assoc 1 resultat))))
      (princ (strcat "\nÉchelle : " (rtos (cdr (assoc 40 resultat)) 2 2)))
    )
    (princ "\nAucune configuration trouvée.")
  )
  (princ)
)

;; Utilisation
(defun c:sauver-config (/ nom echelle)
  (setq nom (getstring T "\nNom du projet : "))
  (setq echelle (getreal "\nÉchelle : "))
  (sauvegarder-configuration nom echelle)
)

(defun c:lire-config ()
  (lire-configuration)
)

Comme les xdata, les dictionnaires sont persistants : ils sont sauvegardés dans le fichier DWG et survivent aux sessions d'AutoCAD. Leur avantage décisif est qu'ils ne sont pas limités en taille (les xdata sont plafonnées à 16 Ko par application et par entité) et qu'ils permettent de stocker des données au niveau du dessin, indépendamment de toute entité.

Xdata ou dictionnaire : comment choisir ?

Critère Xdata Dictionnaire d'extension
Portée Attachées à une entité Au niveau du dessin
Taille maximale 16 Ko par application et par entité Pas de limite pratique
Complexité Simple Plus complexe
Cas d'usage Propriétés personnalisées d'une entité Configuration globale, tables de données

En règle générale : si la donnée est liée à une entité spécifique, utilisez les xdata. Si elle concerne le dessin dans son ensemble, utilisez un dictionnaire.

Lier des entités entre elles par les maintiens

Chaque entité du dessin possède un maintien (handle en anglais) — un identifiant unique sous forme de chaîne hexadécimale. Vous le trouvez dans le code DXF 5 :

(setq donnees (entget (entlast)))
(cdr (assoc 5 donnees))  ; → "1A3F" (par exemple)

Ce maintien reste stable : il ne change pas quand vous sauvegardez et rouvrez le dessin. C'est ce qui le rend idéal pour créer des liens entre entités.

Créer un lien entre deux entités

Imaginons que vous voulez lier un texte d'annotation à la ligne qu'il décrit. L'idée est de stocker le maintien de la ligne dans les xdata du texte :

(defun c:lier-annotation (/ selection-ligne ligne donnees-ligne handle-ligne
                            selection-texte texte donnees-texte)
  (regapp "WIIP_LIEN")

  ;; Sélectionner la ligne
  (setq selection-ligne (entsel "\nSélectionnez la ligne : "))
  (if (null selection-ligne)
    (progn (princ "\nAucune sélection.") (princ) (exit))
  )
  (setq ligne (car selection-ligne))
  (setq donnees-ligne (entget ligne))
  (setq handle-ligne (cdr (assoc 5 donnees-ligne)))

  ;; Sélectionner le texte
  (setq selection-texte (entsel "\nSélectionnez le texte : "))
  (if (null selection-texte)
    (progn (princ "\nAucune sélection.") (princ) (exit))
  )
  (setq texte (car selection-texte))
  (setq donnees-texte (entget texte))

  ;; Attacher le maintien de la ligne au texte via xdata
  (setq donnees-texte
    (append donnees-texte
      (list
        (list -3
          (list "WIIP_LIEN"
            (cons 1005 handle-ligne)    ; Code 1005 = référence de maintien
          )
        )
      )
    )
  )
  (entmod donnees-texte)

  (princ (strcat "\nLien créé vers le maintien " handle-ligne))
  (princ)
)

Le code xdata 1005 est spécialement conçu pour stocker des références de maintiens. Ce n'est pas anodin — AutoCAD le traite différemment d'une simple chaîne (code 1000).

Retrouver l'entité liée

Pour retrouver une entité à partir de son maintien, utilisez handent :

(defun c:suivre-lien (/ selection entite donnees xdata handle-cible cible)
  (setq selection (entsel "\nSélectionnez l'annotation : "))
  (if (null selection)
    (progn (princ "\nAucune sélection.") (princ) (exit))
  )

  (setq entite (car selection))
  (setq donnees (entget entite '("WIIP_LIEN")))
  (setq xdata (assoc -3 donnees))

  (if xdata
    (progn
      (setq handle-cible (cdr (assoc 1005 (cdadr xdata))))
      (setq cible (handent handle-cible))

      (if cible
        (progn
          (princ (strcat "\nEntité liée trouvée : " handle-cible))
          ;; Mettre l'entité cible en surbrillance
          (redraw cible 3)
        )
        (princ "\nL'entité liée n'existe plus dans le dessin.")
      )
    )
    (princ "\nCette entité n'a pas de lien.")
  )
  (princ)
)

WBLOCK et la traduction des maintiens

C'est un point crucial à comprendre. Quand vous utilisez WBLOCK (écrire un bloc dans un fichier externe), AutoCAD traduit les maintiens : les entités reçoivent de nouveaux maintiens dans le fichier de destination. Et grâce au code 1005, AutoCAD sait que la valeur stockée est un maintien et met à jour la référence automatiquement. C'est pour cela qu'il ne faut pas utiliser le code 1000 (chaîne ordinaire) pour stocker un maintien — AutoCAD ne saurait pas qu'il doit le traduire.

En revanche, lors d'une copie interne (par exemple avec _COPY ou (command "_COPY" ...)), il n'y a pas de traduction de maintien. La copie conserve les mêmes valeurs xdata que l'original, y compris les références de maintiens. Les deux entités pointeront donc vers la même cible. C'est un comportement à garder en tête : après une copie, il faudra peut-être mettre à jour les liens manuellement.

Opération Traduction des maintiens (code 1005)
WBLOCK (export vers fichier externe) Oui — les références sont mises à jour
INSERT (import d'un fichier externe) Oui — les références sont mises à jour
COPIE interne (_COPY) Non — les références restent identiques
COPYCLIP / PASTECLIP Oui — traitement similaire à WBLOCK/INSERT

Résumé

Concept Fonction Description
Enregistrer une application regapp Déclare un nom d'application pour les xdata
Lire les xdata (entget ename '("APP")) Récupère les données étendues d'une entité
Dictionnaire racine (namedobjdict) Accède au dictionnaire des objets nommés
Chercher dans un dictionnaire (dictsearch dict "nom") Recherche une entrée par nom
Ajouter au dictionnaire (dictadd dict "nom" xrec) Attache un xrecord au dictionnaire
Maintien d'une entité (cdr (assoc 5 donnees)) Identifiant unique et stable
Entité depuis un maintien (handent "1A3F") Retrouve une entité par son maintien

Dans le prochain chapitre, nous verrons comment réagir automatiquement aux modifications du dessin grâce aux reactors.


Coup de pouce Besoin d'un développement AutoCAD (AutoLISP, ObjectARX, .NET, VBA) ? Contactez-moi pour un devis gratuit.