Aller au contenu

Première incursion dans le C#


krunch

Messages recommandés

Bonjour

 

J'ai mis un orteil dans le .Net à l'occasion d'un projet apriori simple : un highlight-nested.

 

J'avais tenté en Lisp mais la solution qui consiste à reproduire l'entité est décidemment trop poussive, pas très satisfaisante, et vite limitée (par exemple si je veux maintenant highliter une sous-entité depuis une sélection entsel il faut calculer sa matrice à partir de son niveau d'imbrication en multipliant des matrices (mxm) ... ce qui est particulièrement lourd).

 

Bref, le .Net semble bien être la solution pour ça, c'est pourquoi j'ai jeté un oeil sur 2 codes trouvés sur le net :

- le 1er que j'avais indiqué de James Allen (que je reproduis ci dessous) mais qui ne marche pas chez moi (ACad 2010)

- un 2è de Kean Walmsley, qui marche ..

 

J'ai donc installé Visual Studio 2012, suivi le tutoriel de (gile) en lien sur ce forum, et réussi à générer un dll avec les 2 codes (malgré quelques messages d'erreur).

 

Mon problème c'est que j'ai besoin de la structure du code de JA qui marche pas, à savoir une fonction Lisp à laquelle transmettre l'entité + sa liste d'imbrication (le dernier élément du renvoi nentsel) en argument... Alors que le code de KW inclut l'entrée utilisateur (sélection) qui est faite en C#.

 

Voilà le message d'erreur que j'obtiens avec le dll de JA (ou avec mon dll compilé c'est pareil) :

System.MissingMethodException: Méthode introuvable : 'Void 
Autodesk.AutoCAD.DatabaseServices.SubentityId..ctor(Autodesk.AutoCAD.DatabaseServices.SubentityType, Int32)'.
  à HighlightNested.LispFuncClass.Highlight(ResultBuffer LspArgs, Boolean hl)
  à Autodesk.AutoCAD.Runtime.CommandClass.InvokeWorker(MethodInfo mi, Object commandObject, Boolean bLispFunction)
  à Autodesk.AutoCAD.Runtime.CommandClass.InvokeWorkerWithExceptionFilter(MethodInfo mi, Object commandObject, Boolean bLispFunction)
  à Autodesk.AutoCAD.Runtime.CommandClass.CommandThunk.InvokeLisp(); erreur: 
Demande ADS erronée

 

 

Je remarque que le code de JA 'cite' celui de KW, mais qu'il n'utilise pas du tout les mêmes données ni les mêmes méthodes (pour inverser la liste par exemple), ni le même ordre dans la séquence 'Using (Transaction ..)'

Je ne connais pas le C#, c'était donc une approche intuitive qui s'arrète là ..

 

 

Autre chose, j'ai aussi 2 avertissements comme ça sur acdbmgd et acmgd au moment de la génération :

C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(1578,5): warning MSB3270: Il existe une différence entre l'architecture de processeur du projet en cours de génération "MSIL" et l'architecture de processeur de référence "acmgd", "AMD64"...

Apparemment il compile avec Framework\v4.0 alors que j'ai bien spécifié Framework\v3.5 dans les propriétés (cf tutoriel) ..

 

 

 

Le code de JA :

//Tested on 2007-2010

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using System.Collections.Generic;

namespace HighlightNested
{
   public static class LispFuncClass
   {
       ///------------------------------------------------------
       /// Arguments: Output from entsel, nentsel, or nentselp.
       /// Returns: Ename if successful, otherwise nil.
       /// Examples: 
       ///     (apply 'nenthl (nentsel))
       ///     (nenthl (nentsel))
       ///     (nentuhl (nentsel))
       ///
       /// James Allen - 10Jun09
       /// Malicoat-Winslow Engineers, P.C.
       ///------------------------------------------------------

       [LispFunction("nenthl")]
       public static object nenthl(ResultBuffer LspArgs)
       { return Highlight(LspArgs, true); }

       [LispFunction("nentuhl")]
       public static object nentuhl(ResultBuffer LspArgs)
       { return Highlight(LspArgs, false); }

       public static object Highlight(ResultBuffer LspArgs, bool hl)
       {
           //Initialize
           if (LspArgs == null) return null;
           int an = 0, ld = 0;//argument number, list depth
           ObjectId id = new ObjectId();
           List<ObjectId> path = new List<ObjectId>();

           //Process Arguments
           foreach (TypedValue tv in LspArgs)
           {
               switch (an)
               {
                   case 0: //Get ObjectId
                       if (tv.TypeCode == (int)LispDataType.ListBegin)
                           break;
                       else if (tv.TypeCode == (int)LispDataType.ObjectId)
                           id = (ObjectId)tv.Value;
                       else return null;
                       an++; break;
                   case 1: //Ignore Pick Point
                       an++; break;
                   case 2: //Ignore Matrix
                       if (tv.TypeCode == (int)LispDataType.ListBegin)
                           ld++;
                       else if (tv.TypeCode == (int)LispDataType.ListEnd)
                       { ld--; if (ld == 0) an++; }
                       break;
                   case 3: //Get Nest Path
                       if (tv.TypeCode == (int)LispDataType.ListBegin)
                           ld++;
                       else if (tv.TypeCode == (int)LispDataType.ListEnd)
                       { ld--; if (ld == 0) an++; }
                       else if (tv.TypeCode == (int)LispDataType.ObjectId)
                           path.Add((ObjectId)tv.Value);
                       break;
                   default: break;
               }
           }
           //Highlight
           Database db = id.Database;
           using (Transaction t = db.TransactionManager.StartTransaction())
           {
               Entity e = (Entity)id.GetObject(OpenMode.ForRead);
               if (e.GetType() == typeof(AttributeReference) && (path.Count == 0 || !e.OwnerId.Equals(path[0])))
                   path.Insert(0, e.OwnerId);
               if (path.Count > 0)
               {
                   //Highlight Nested 
                   //Credit: Kean Walmsley and Others
                   //http://through-the-interface.typepad.com/through_the_interface/2006/12/highlighting_a_.html
                   //-------------------------------------------------------
                   path.Reverse(); path.Add(id);
                   e = (Entity)path[0].GetObject(OpenMode.ForRead);
                   SubentityId si = new SubentityId(SubentityType.Null, 0);
                   FullSubentityPath fsp = new FullSubentityPath(path.ToArray(), si);
                   if (hl) e.Highlight(fsp, true);
                   else e.Unhighlight(fsp, true);
                   //-------------------------------------------------------
               }
               else
               {
                   if (hl) e.Highlight();
                   else e.Unhighlight();
               }
               t.Commit();
           }
           //Return ObjectId
           return new TypedValue((int)LispDataType.ObjectId, id);
       }
   }
}

Lien vers le commentaire
Partager sur d’autres sites

Autre piste : pourquoi ne pas fournir directement à la nouvelle fonction Lisp la liste d'imbrication au format requis ?

Ca donnerait un appel comme ça :

(setq ent (nentsel))
(setq ent (cons (car ent) (reverse (cadddr ent)))
(nenthl ent)

 

Autre hypothèse :

Peut on fournir un argument point à la méthode editor.GetNestedEntity qu'utilise KW, de la même façon qu'avec nentselp (quand on lui fournit un point on zappe l'entrée utilisateur) ??

Ca donnerait un appel comme ça :

(setq ent (cadr (nensel))
(nenthl ent)

Lien vers le commentaire
Partager sur d’autres sites

Salut,

 

Bienvenue au club, et bon courage car comme tu le vois, les choses sont moins facile qu'en LISP...

 

J'ai fait quelques test et j'ai effectivement noté u n problème avec AutoCAD 2010 (tout fonctionne très bien avec les versions ultérieures). Je suis étonné que tu aies réussi à faire fonctionner celle de Kean et pas celle de James, le code incriminé est identique dans les deux.

 

Il semblerait que, bien qu'existant dans la bibliothèque acdbmgd.dll, les constructeurs surchargés de la structure SubEntityId ne le soit pas par AutoCAD.

J'ai réussi à faire le faire fonctionner en utilisant le constructeur par défaut (sans arguments)

Il faut remplacer :

SubentityId si = new SubentityId(SubentityType.Null, 0);

par :

SubentityId si = new SubentityId();

 

Sinon les avertissements que tu as ne sont que des avertissements et ne devraient pas perturber le fonctionnement de la DLL.

D'après ce que je comprends, la plateforme cible de acdbmgd.dll et acmgd.dll n'est pas la même que la plateforme d'exécution. Dans ObjectARX 2010 on trouve ces dlls dans deux répertoires : inc-win32 et inc-x64, tu n'as peut-être pas choisi le bon quand tu as fais ton modèle.

Dans tous les cas, ça n'a pas d'importance ces dlls, référencées en Copie locale, ne sont utilisées que par Visual Studio le temps de l'édition (pour pouvoir fournir l'aide sur les classes contenues dans ces bibliothèques) et de la compilation.

À l’exécution, ce sont les DLLs contenues dans le Global Assemby Cache (GAC) qui sont utilisée en fonction de de la plateforme courante (et de la version d'AutoCAD).

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Apparemment tu as lié ta dll à la version x64 de AcMgd (celle qui se trouve dans C:\ObjectARX 2010\inc-x64). Donc VS te propose la version x64 du constructeur, qui prends certainement en paramètre un pointeur 64 bits. Mais tu l'exécutes dans un AutoCAD 32 bits, c'est donc la version x86 qui est appelée, avec un pointeur 32 bits en paramètre. Il te suffit donc de remplacer ta référence à C:\ObjectARX 2010\inc-x64\AcMgd.dll par C:\ObjectARX 2010\inc-win32\AcMgd.dll.

 

Si tu veux supporter les OS 32 et 64 bits, tu devras compiler 2 dll différentes, une en x86 liée aux références inc-win32 et une en x64 liée aux références inc-x64.

 

Le problème se pose pour AutoCAD 2009 et 2010, car quelques fonctions ont des signatures différentes entre x86 et x64.

Maxence DELANNOY

Développement de compléments aux logiciels Autodesk : AutoCAD, Revit, Inventor, Vault, Navisworks... et autres logiciels de CAO

WIIP - http://wiip.fr

Lien vers le commentaire
Partager sur d’autres sites

Salut

 

J'ai remplacé la ligne du SubEntityId par SubentityId si = new SubentityId(); mais j'ai toujours la même erreur...

 

Sinon oui, le code de Kean marche correctement (malgré que j'aie les mêmes avertissements).

 

Et pour ce que tu précises dans ton tutorial à propos de la provenance de AcDbMgd.dll et AcMgd.dll ("ou, mieux, dans le dossier ObjectARX20##\inc.."), je ne vois pas les dossiers mentionnés dans le répertoire d'installation, j'ai donc pris ceux du répertoire racine. Je les aurais bien téléchargé mais ils ne sont pas fournis sur la page Autodesk...

Mais bon puisque c'est pas grave ..

 

 

Donc non, ça ne marche pas .. ACac 2010 serait-il déjà trop vieux ?

Lien vers le commentaire
Partager sur d’autres sites

Mais tu l'exécutes dans un AutoCAD 32 bits

Non c'est bien un 64 bits

 

Par contre, pour en revenir à ces .dll j'ai un répertoire "C:\Autodesk\AutoCAD_2010_French_SLD_WIN_64bit" (en plus de "C:\Program Files\AutoCAD 2010") qui contient aussi les 2 dll (mais pas de répertoire "inc-win32"/64)..

Il faut prendre ceux là ?

Lien vers le commentaire
Partager sur d’autres sites

Autre piste : pourquoi ne pas fournir directement à la nouvelle fonction Lisp la liste d'imbrication au format requis ?

Ca donnerait un appel comme ça :

(setq ent (nentsel))
(setq ent (cons (car ent) (reverse (cadddr ent)))
(nenthl ent)

 

Autre hypothèse :

Peut on fournir un argument point à la méthode editor.GetNestedEntity qu'utilise KW, de la même façon qu'avec nentselp (quand on lui fournit un point on zappe l'entrée utilisateur) ??

Ca donnerait un appel comme ça :

(setq ent (cadr (nensel))
(nenthl ent)

 

1_ On pourrait en changeant la partie du code C# qui traite les arguments passés à la fonction, mais je ne vois pas ce que ça apporterait, en l'état, on peut passer aussi bien la liste retournée par entsel que celle retournée par nentsel ou celle retournée par nentselp. Pour quoi vouloir faire moins polyvalent ?

 

2_ On ne peut pas le faire en l'état, il faut aussi modifier le code : ajouter le point comme argument à la méthode InternalHighlightNested et changer le mode sélection :

private static void InternalHighlightNested(bool isEntity, Point3d pickPoint)
{
   Document doc = Application.DocumentManager.MdiActiveDocument;
   Editor ed = doc.Editor;

   PromptNestedEntityOptions opts = new PromptNestedEntityOptions("");
   opts.NonInteractivePickPoint = pickPoint;
   opts.UseNonInteractivePickPoint = true;
   PromptNestedEntityResult rs = ed.GetNestedEntity(opts);

   // ...
}

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

on peut passer aussi bien la liste retournée par entsel que celle retournée par nentsel

Oui tu as raison, c'était juste parce que les arguments point et matrice ne servent à rien ..

 

.. ajouter le point comme argument à la méthode InternalHighlightNested et changer le mode sélection ..

Alors au pire si la 1ère solution ne marche pas je pourrais peut être passer par là ?

En tout cas pas d'amélioration en changeant le SubentityId

Lien vers le commentaire
Partager sur d’autres sites

Si tu veux supporter les OS 32 et 64 bits, tu devras compiler 2 dll différentes, une en x86 liée aux références inc-win32 et une en x64 liée aux références inc-x64.

Non, il n'est pas nécessaire de compiler 2 DLLs différentes, comme je le disais, les références sont en Copie locale = False et ne servent qu'à permettre à Visual Studio d'accéder aux bibliothèques pour fournir l'aide à l'édition (intellisense) et contrôler les erreurs de compilation.

Quand l'application est exécutée le compilateur JIT va chercher les DLLs correspondant à la plateforme d'exécution (32 ou 64 bits) et à la version d'AutoCAD dans le GAC.

C'est ce processus qui permet aux application .NET une plus grande compatibilité que les applis ObjectARX.

 

Je le fais régulièrement et je viens de le refaire pour ces tests en compilant sur un poste Windows 7 64 bits (en référençant les DLLs sur ObjectARX 2010\inc-x64) et en exécutant la même dll sur un poste XP 32 bits avec A2010 et A2012

 

 

Et pour ce que tu précises dans ton tutorial à propos de la provenance de AcDbMgd.dll et AcMgd.dll ("ou, mieux, dans le dossier ObjectARX20##\inc.."), je ne vois pas les dossiers mentionnés dans le répertoire d'installation, j'ai donc pris ceux du répertoire racine. Je les aurais bien téléchargé mais ils ne sont pas fournis sur la page Autodesk...

Autodesk ne fournit de support que pour les 3 dernières versions, c'est pourquoi on ne trouve plus ObjectARX 2010 sur leur site.

Les répertoires inc, inc-win32 et inc-x64 ne se trouvent que dans ObjectARX, dans le répertoire d'installation (C:\Program Files\ AutoCAD 2010), elles sont directement dans le dossier AutoCAD 2010.

 

Si tu veux, je peux te faire parvenir la solution que j'ai faite et/ou ObjectARX 2010.

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Si tu veux, je peux te faire parvenir la solution que j'ai faite et/ou ObjectARX 2010.

C'est vrai que pour le moment je ne sais plus trop quoi essayer, et la solution de l'argument point serait en fait assez limitative ..

Alors oui volontiers (il suffit peut être de mettre à jour les ObjectARX 2010 ?)

Lien vers le commentaire
Partager sur d’autres sites

Ça marche avec ta version !

 

Par contre je n'ai pas téléchargé ObjectARX 2010, et je ne vois pas ce que tu as changé dans HighLightNestedEntity.sln, y a t-il des modifs ?

 

Sinon pour comprendre :

- le code de Kean n'est pas nécessaire si je refais la dll depuis HighLightNestedEntity.sln (il est dans 'commands.cs') ?

- dans ce cas si je supprime ta .dll (de ce jour à 15h25) de bin/Debug et que je la régénère ... la nouvelle .dll m'indique toujours 15h25 en propriété de fichier (alors qu'il est plus !), comment ça se fait ?

 

Merci pour ton envoi

Lien vers le commentaire
Partager sur d’autres sites

Je te conseille quand même de télécharger ObjectARX 2010, tu y trouveras la pauvre mais unique documentation sur les bibliothèques .NET d'AutoCAD (ObjectARX 2010\docs\arxmgd.chm).

 

Je suis parti d'un modèle pour Visual Studio 2012 Express comme ceux décris dans le tuto.

J'ai changé les DLLs référencées pour pointer sur celles contenues dans C:\Program Files\AutoCAD 2010\ de façon à ce que ça fonctionne aussi chez toi.

Je n'ai rien changé dans les codes, c'est du copié/collé.

 

Si tu veux supprimer le code de Kean, il suffit de sélectionner la classe Commands dans l'explorateur de solutions > clic droit supprimer.

Si la dll générée a la même heure que la précédente, c'est que rien n'a dû être modifié entre les deux compilation.

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Je te conseille quand même de télécharger ObjectARX 2010 ..

Ok, justement je me demandais où se trouvait la doc pour tout ça.

 

Je suis parti d'un modèle pour Visual Studio 2012 Express comme ceux décris dans le tuto.

J'ai changé les DLLs référencées pour pointer sur celles contenues dans C:\Program Files\AutoCAD 2010\ de façon à ce que ça fonctionne aussi chez toi.

C'est aussi ce que j'ai fait, j'ai suivi scrupuleusement ton tuto ..

 

En tout cas ça marche, merci beaucoup pour ton aide

Lien vers le commentaire
Partager sur d’autres sites

Rebonjour

 

Comme souvent en faisant des essais plus poussés on trouve des problèmes ...

 

On peut faire marcher nenthl/nentuhl avec le Lisp ci-dessous en activant la partie 'Version simple' et en désactivant le reste, nenthl/nentuhl sont dans le dernier .dll posté par (gile):

 

- Polys old style (Vertex) : ça marche pas, c'est pas prévu dans le C#

- Tableaux : le highlight fait apparaître des entités temporaires autour du tableau (idem sélection : pochages, indications de lignes et colonnes etc..), si on passe un de ces trucs (<Nom d'entité : 0>) à nenthl ça fait une erreur fatale

- Entités des bloc annotatifs : une espèce de fantôme apparait avec le highlight (idem sélection : avec les entités à plusieurs échelles d'annotation je crois)

- RefAttributs des blocs annotatifs : pas de highlight (mais le "fantôme" est là)

- RefAttributs avec Champ : le champ n'est pas highlité

 

Alors j'ai fait une nouvelle version qui les corrige en Lisp (sauf le dernier point).

Pour les 2 premiers je gère la liste d'imbrication en Lisp à la place du C# (ce n'était pas nécessaire mais ça m'arrange), j'ai adapté (artisanalement) le C# principalement en enlevant du code.

Pour les blocs annotatifs c'est plus compliqué, j'ai trouvé la parade en désactivant la propriété annotative le temps du highlight et en la réactivant ensuite, par contre ça oblige à stocker la propriété annotative.

 

 

Le Lisp marche avec les nouvelles fonctions high1/high0 qui se trouvent en dessous.

 

 

; Boucle (grread) avec Highlight des entités survolées, imbriquées ou non (.Net)
; Filtrage des XRefs

; Réserves :
; - Tableaux : nentselp ne détecte pas le texte normal (sans Champ) contenu dans les tableaux
; - RefAttss : le highlight ne marche pas sur les Champs
(defun c:go (/ *ERROR* out grr cod obj rol ctx)

   (defun *ERROR* (msg)
       (if rol (High rol nil))
       (print msg)
   )
   
   (while (not out)
       (setq grr (grread t 15 2)
             cod (car grr))
       (cond
           
           ; Enter/Espace/Clic-droit
           ((or (= cod 25) (member grr '((2 13)(2 32))))
            	(*ERROR* "sortie")
               (setq out T)
           )
           
           ; Rollover
           ((= cod 5)
            	(setq cod (nentselp (cadr grr))
                     ctx (cadddr cod)
               )

;;;             	; Version simple
;;;             	(setq obj cod)
;;;             	(or (equal obj rol)
;;;                    (progn
;;;                        (if rol (nentuhl rol))
;;;                        (setq rol obj)
;;;                        (if rol (nenthl rol))
;;;                    )
;;;                )
               
               ; Liste d'imbrication +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            	(if (and ctx (setq obj (vl-some '(lambda(u) (if (assoc 1 (tblsearch "BLOCK" (cdr (assoc 2 (entget u))))) u)) (reverse ctx))))
                   ; Filtrage des entités de XRef : obj = dernière XRef
                   (setq obj (member obj ctx))
                   (if (setq obj (car cod))
                       (setq cod (entget obj)
                             obj (cond
                                     ; Filtrage des entités temporaires de Tableaux
                                     ((not cod) nil)
                                     ; RefAttributs Vertex + autres
                                     ((= (cdr (assoc 0 cod)) "ATTRIB") (cons obj (cons (cdr (assoc 330 cod)) ctx)))
                                     ((= (cdr (assoc 0 cod)) "VERTEX") (cons (cdr (assoc 330 cod)) ctx))
                                     (T (cons obj ctx))
                           	  )
                       )
                   )
            	)
            	; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

            	; obj : liste d'imbrication (ent [ref0 .. refN])
            	; rol : liste d'imbrication du highlight courant + ann (ann ent [ref0 .. refN])
            	; ann : liste (entget blc '("AcadAnnotative")) avec la XData du bloc si ent imbriquée dans un bloc annotatif / nil sinon
            	(or (equal obj (cdr rol))
                   (progn
                       (if rol (setq rol (High rol nil)))
                       (if obj (setq rol (High obj T)))
                   )
               )
           )            
       )
   )
   (princ)
)
(defun High (obj on / ann)
   ; Pré-traitement Bloc annotatif
   (if (setq ann (if on
                     (if (cdr obj) (Get_Annot (cdr (assoc 330 (entget (tblobjname "block" (cdr (assoc 2 (entget (last obj))))))))))
                     (car obj)
       ))
       (entmod (subst (list -3 (list "AcadAnnotative" '(1000 . "AnnotativeData") '(1002 . "{") '(1070 . 1) (cons 1070 (if on 0 1)) '(1002 . "}"))) (assoc -3 ann) ann))
   )
   ; Highlight
   (if on
       (High1 obj)
       (High0 (cdr obj))
   )
   ; Renvoi
   (if on (cons ann obj))
)
(defun Get_Annot (obj / out)
   (if (= 1 (cdr (assoc 1070 (reverse (cadr (assoc -3 (setq out (entget obj '("AcadAnnotative")))))))))
       out
   )
)

 

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using System.Collections.Generic;

namespace HighLightNested
{
   public class Functions
   {
       ///-----------------------------------------------------
       /// Fonctions high1 + high0
       /// Argument: Liste d'imbrication (ent [ref0 .. refN])
       /// Returns: Ename if successful, otherwise nil. 
       /// Exemples: 
       ///     (high1 (cons (car (setq in (nentsel))) (cadddr in)))
       ///     (high1 (list (car (entsel))))
       /// ou  (high1 (car (entsel)))
       /// ou  (high1 (entsel))
       ///     (high0 ...)
       ///----------------------------------------------------
       /// James Allen - 10Jun09
       /// Malicoat-Winslow Engineers, P.C.
       ///------------------------------------------------------

       [LispFunction("high1")]
       public static object nenthl(ResultBuffer LspArgs)
       { return Highlight(LspArgs, true); }

       [LispFunction("high0")]
       public static object nentuhl(ResultBuffer LspArgs)
       { return Highlight(LspArgs, false); }

       public static object Highlight(ResultBuffer LspArgs, bool hl)
       {
           //Initialize
           if (LspArgs == null) return null;
           int an = 0, ld = 0;//argument number, list depth
           //ObjectId id = new ObjectId();
           List<ObjectId> path = new List<ObjectId>();

           //Process Arguments
           foreach (TypedValue tv in LspArgs)
           {
               switch (an)
               {
                   //// Géré en Lisp ..
                   //case 0: //Get ObjectId
                   //    if (tv.TypeCode == (int)LispDataType.ListBegin)
                   //        break;
                   //    else if (tv.TypeCode == (int)LispDataType.ObjectId)
                   //        id = (ObjectId)tv.Value;
                   //    else return null;
                   //    an++; break;
                   //case 1: //Ignore Pick Point
                   //    an++; break;
                   //case 2: //Ignore Matrix
                   //    if (tv.TypeCode == (int)LispDataType.ListBegin)
                   //        ld++;
                   //    else if (tv.TypeCode == (int)LispDataType.ListEnd)
                   //    { ld--; if (ld == 0) an++; }
                   //    break;
                   case 0: //Get Nest Path
                       if (tv.TypeCode == (int)LispDataType.ListBegin)
                           ld++;
                       else if (tv.TypeCode == (int)LispDataType.ListEnd)
                       { ld--; if (ld == 0) an++; }
                       else if (tv.TypeCode == (int)LispDataType.ObjectId)
                           path.Add((ObjectId)tv.Value);
                       break;
                   default: break;
               }
           }
           //Highlight
           Database db = path[0].Database;
           using (Transaction t = db.TransactionManager.StartTransaction())
           {
               //// Géré en Lisp ..
               //Entity e = (Entity)id.GetObject(OpenMode.ForRead);
               //if (e.GetType() == typeof(AttributeReference) && (path.Count == 0 || !e.OwnerId.Equals(path[0])))
               //    path.Insert(0, e.OwnerId);
               if (path.Count > 0)
               {
                   //Highlight Nested 
                   //Credit: Kean Walmsley and Others
                   //http://through-the-interface.typepad.com/through_the_interface/2006/12/highlighting_a_.html
                   //-------------------------------------------------------
                   path.Reverse();
                   //// Géré en Lisp ..
                   //path.Add(id);
                   Entity e = (Entity)path[0].GetObject(OpenMode.ForRead);
                   //SubentityId si = new SubentityId(SubentityType.Null, 0);
                   SubentityId si = new SubentityId();
                   FullSubentityPath fsp = new FullSubentityPath(path.ToArray(), si);
                   if (hl) e.Highlight(fsp, true);
                   else e.Unhighlight(fsp, true);
                   //-------------------------------------------------------
               }
               else
               {
                   Entity e = (Entity)path[0].GetObject(OpenMode.ForRead);
                   if (hl) e.Highlight();
                   else e.Unhighlight();
               }
               t.Commit();
           }
           //Return ObjectId
           return new TypedValue((int)LispDataType.ObjectId, path[0]);
       }

   }
}

 

Juste un truc à propos de ce code : on peut enlever le foreach, switch .. dans ce cas où il n'y a plus qu'1 seul argument ?

Lien vers le commentaire
Partager sur d’autres sites

Juste un truc à propos de ce code : on peut enlever le foreach, switch .. dans ce cas où il n'y a plus qu'1 seul argument ?

 

Dans la mesure où il n'y a plus qu'un seul case dans l'expression switch, elle est inutile (swicth est un peu l'équivalent de cond).

 

Par contre pour le foreach ce n'est pas possible. Tu parles d'un seul argument en LISP mais c'est une liste. Le ou les arguments LISP sont toujours convertis en ResultBuffer dans une fonction .NET avec l'attribut LispFunction.

Un ResultBuffer est conteneur de TypedValue.

 

Le type TypedValue est une sorte "d'emballage" pour les objets de types différents (on peut y voir des similitudes avec les paires pointées du LISP ou les variants de l'interface COM même si c'est fondamentalement différent). Ce type a deux propriétés : TypeCode (un entier qui correspond au type réel de la valeur) et Value (la valeur convertie en type Object, on parle de boxing). Cet "emballage" permet à la fois de faire des conteneurs d'objets de type différents (ce qui n'est possible en .NET qu'avec le type Object) et d'accéder au type sous-jacent de la valeur.

 

Le type ResultBuffer est donc un conteneur de TypedValue qui permet de faire des collections avec la plupart des types basiques utilisée dans AutoCAD. Outre les définitions de fonctions LISP, les ResultBuffer sont utilisés pour les Xdata et les Xrecord.

Dans le cas des arguments de fonctions LISP, le ResultBuffer doit contenir toutes les informations permettent de décrire sans équivoque ces arguments, y compris les parenthèses ouvrantes fermantes qui délimitent les listes. Dans le cas présent où l'argument est une liste de noms d'entités, le ResultBuffer contiendra une TypedValue pour la parenthèse ouvrante, puis une par nom d'entité et enfin, une pour la parenthèse fermante.

Le foreach est donc inévitable pour remplir la liste d'ObjectId (path) et doit contenir une clause if pour écarter les parenthèses. Mais on peut le simplifier :

foreach (TypedValue tv in LspArgs)
{
   // si le type est ObjectId (équivalent de ENAME)
   if (tv.TypeCode == (int)LispDataType.ObjectId)
       // la valeur, après unboxing, est ajoutée à la liste
       path.Add((ObjectId)tv.Value); 
}

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Fatche .. c'est vraiment pas simple.

 

Par la suite je potasserai l'aide avant d'y revenir, et tes explications qui dégrossissent énormément ..

 

Donc d'après ce que je comprends notamment, (int)LispDataType.ListBegin est le 'code de groupe' qui correspond à une parenthèse ouvrante ?

 

Et sans abuser, j'aurais juste une dernière question dans la continuité sur les entrée/sortie : à l'inverse, si je veux renvoyer la liste d'entités 'path' plutôt que l'entité 'path[0]' dans le return ?

Lien vers le commentaire
Partager sur d’autres sites

Donc d'après ce que je comprends notamment, (int)LispDataType.ListBegin est le 'code de groupe' qui correspond à une parenthèse ouvrante ?

Oui, LispDataType est une énumération dont chaque membre correspond à un entier qu'on récupère en convertissant (cast) le membre de l'énumération en entier. Ceci permet d'écrire un code plus explicite qu'en écrivant directement l'entier (5016 pour ListBegin). Ceci dit, pour les TypedValue utilisées avec les filtres de sélection, les Xdata ou les Xrecord, j'ai tendance à utiliser directement les entiers (qui correspondent aux codes de groupe DXF et que je connais par cœur) plutôt que les membres de l'énumération DxfCode.

Les membres de l'énumération LispDataType et les entiers correspondants :

Angle      	5004
DottedPair    	5018
Double       	5001
Int16           5003
Int32      	5010
ListBegin   	5016
ListEnd 	5017
Nil    		5019
None     	5000
ObjectId  	5006
Orientation     5008
Point2d 	5002
Point3d 	5009
SelectionSet	5007
T_atom       	5021
Text     	5005
Void     	5014

 

Et sans abuser, j'aurais juste une dernière question dans la continuité sur les entrée/sortie : à l'inverse, si je veux renvoyer la liste d'entités 'path' plutôt que l'entité 'path[0]' dans le return ?

Oui, facile. il suffit de remplacer object par ResultBuffer comme type de retour dans les déclarations de méthodes et retourner le ResultBuffer passé en argument :

        [LispFunction("high1")]
       public static ResultBuffer nenthl(ResultBuffer LspArgs)
       { return Highlight(LspArgs, true); }

       [LispFunction("high0")]
       public static ResultBuffer nentuhl(ResultBuffer LspArgs)
       { return Highlight(LspArgs, false); }

       public static ResultBuffer Highlight(ResultBuffer LspArgs, bool hl)
       {
           //...

           //Return ResultBuffer 
           return LspArgs;
       }

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Oui, facile. il suffit de remplacer object par ResultBuffer comme type de retour dans les déclarations de méthodes et retourner le ResultBuffer passé en argument

Ok .. il faut adapter la déclaration d'entrée + celle de la routine, compris

 

Par contre 'LspArgs' fait référence à l'argument d'entrée.

Dans ce cas précis il ne subit pas vraiment de calcul (il est juste inversé), mais imaginons qu'il y ait un vrai calcul ou autre, et que je veuille renvoyer le résultat 'path' ..

Dans ce cas il faut fabriquer un nouveau ResultBuffer de TypedValue avec un foreach c'est ça ?

Lien vers le commentaire
Partager sur d’autres sites

Oui tu peux utiliser foreach :

ResultBuffer result = new ResultBuffer();
foreach (TypedValue tv in path)
{
   result.Add(tv);
}
return result;

mais le type ResultBuffer a un constructeur qui accepte comme argument un tableau de TypedValue (TypedValue[]), tu peux retourner directement un nouveau ResultBuffer construit avec path converti en Array:

return new ResultBuffer(path.ToArray());

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Alors non : j'ai une erreur dans les 2 cas Impossible de convertir le type 'Autodesk.AutoCAD.DatabaseServices.ObjectId' en'Autodesk.AutoCAD.DatabaseServices.TypedValue'

 

Et avec le tableau j'ai une erreur La méthode surchargée correspondant le mieux .. possède des arguments non valides (peut être à cause de la version)

 

C'est plus pour comprendre les syntaxes d'entrée/sortie, je précise que pour le moment je n'ai pas le niveau pour m'en servir ..

Lien vers le commentaire
Partager sur d’autres sites

Au temps pour moi, j'ai cru que path était un tableau de TypedValue...

Il faut donc bien passer par foreach :

ResultBuffer result = new ResultBuffer();
foreach (ObjectId id in path)
{
   result.Add(new TypedValue((int)LispaDataType.ObjectId, id));
}
return result;

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Créer un compte ou se connecter pour commenter

Vous devez être membre afin de pouvoir déposer un commentaire

Créer un compte

Créez un compte sur notre communauté. C’est facile !

Créer un nouveau compte

Se connecter

Vous avez déjà un compte ? Connectez-vous ici.

Connectez-vous maintenant
×
×
  • Créer...

Information importante

Nous avons placé des cookies sur votre appareil pour aider à améliorer ce site. Vous pouvez choisir d’ajuster vos paramètres de cookie, sinon nous supposerons que vous êtes d’accord pour continuer. Politique de confidentialité