Communauté
AutoCAD – tous produits - Français
Bienvenue sur les forums AutoCAD d’Autodesk. Partagez vos connaissances, posez des questions, et explorez les sujets AutoCAD populaires.
annuler
Affichage des résultats de 
Afficher  uniquement  | Rechercher plutôt 
Vouliez-vous dire : 

C# Créer un SelectionSet pour extraire tous les textes à une coordonnée précise

4 RÉPONSES 4
RÉSOLU
Répondre
Message 1 sur 5
Jean-Marc68
864 Visites, 4 Réponses

C# Créer un SelectionSet pour extraire tous les textes à une coordonnée précise

Salut à toutes et à tous,

 

Sous AcadMap2010 je cherche à développer une application en C# devant, entre autre, récupérer des textes en fonction des coord de leur point d'insertion.

 

Une image vaut 1000 mots, donc voici un exemple :

PP.jpg

 

Dans l'image ci-dessus se trouvent 3 éléments : 2 single line text et un bloc. Les 3 ont leur point d'insertion à la même place. Le numéro de point est dans ce cas-ci 185 (texte horizontal) et son PCode est 175 (texte en biais)

Ce que je dois faire :

Extraire le numéro de point et son PCode.

 

Méthode :

L'utilisateur clique sur le(s) numéro(s) de point(s) à sélectionner.

Un SelectionSet en découle

Pour chaque point:

  • je déduis le point3d qui me donne les coordonnées du point d'insertion du texte
    (Jusqu'ici tout allait bien. C'est à partir d'ici que ça coince)
  • Je recherche tous les textes qui ont les mêmes coordonnées que le point3d créé et en fais un SelectionSet
    (il doit donc trouver les 2 textes puisqu'ils ont tous les 2 les mêmes coordonnées)
  • Je teste si le point trouvé est le même objet que le point cliqué
  • Si le point est différent de l'objet cliqué, c'est que c'est bien le PCode
  • Je lie les 2 données et je continue de traitement à faire

 

Le code devant permettre de trouver les textes en fonction de la coordonnée :

private SelectionSet GetTxtByPosition(Point3d txtInsPoint, Editor ed)
        {
            PromptSelectionResult ws = default(PromptSelectionResult);
            TypedValue[] acTypValAr = new TypedValue[4];

            acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "TEXT"), 0);
            acTypValAr.SetValue(new TypedValue((int)DxfCode.XCoordinate, txtInsPoint.X), 1);
            acTypValAr.SetValue(new TypedValue((int)DxfCode.YCoordinate, txtInsPoint.Y), 2);
            acTypValAr.SetValue(new TypedValue((int)DxfCode.ZCoordinate, txtInsPoint.Z), 3);

            SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);
            ws = ed.SelectAll(acSelFtr);
            if (ws.Status == PromptStatus.OK)
            {
                return ws.Value;
            }
            return null;
        }

 

Le message d'erreur "Object reference not set to an instance of an object." sort sur la ligne ws = ed.SelectAll(acSelFtr);

 

Or acSelFtr me donne : acSelFtr = {Autodesk.AutoCAD.EditorInput.SelectionFilter}

                                          Non-Public members

                                              m_value = {Autodesk.AutoCAD.DatabaseServices.TypedValue[4]}

                                                  [0] = {(0,TEXT)}

                                                  [1] = {(10,230577.38)}

                                                  [2] = {(20,5347851.622)}

                                                  [3] = {(30,0)}

et ed me donne ed = {Autodesk.AutoCAD.EditorInput.Editor}     qui est complet aussi

 

Je ne comprend donc pas où est le problème.

 

 

 

Voulant contourner le problème en utilisant un SelectCrossingWindow avec une très petite fenêtre autour du point d'insertion, j'ai écris le code suivant

private SelectionSet GetTxtByPosition(Point3d txtInsPoint, Editor ed)
        {
            Point3d p1 = new Point3d(txtInsPoint.X - 0.0001, txtInsPoint.Y - 0.0001, 0.0);
            Point3d p2 = new Point3d(txtInsPoint.X + 0.0001, txtInsPoint.Y + 0.0001, 0.0);
            PromptSelectionResult ws = default(PromptSelectionResult);
            TypedValue[] acTypValAr = new TypedValue[1] { new TypedValue((int)DxfCode.Start, "TEXT") };
            
            SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);
            ws = ed.SelectCrossingWindow(p1,p2,acSelFtr);
            if (ws!= null)
            {
                return ws.Value;
            }
            return null;
        }

 Mais ici il retourne invariablement un null.

 

Or si j'enlève le filtre, il trouve le bloc, et si je sélectionne le PCode au lieu du numéro de point, il me trouve la même coordonnée que le point. Les 2 textes ont donc bel et bien les mêmes coordonnées, mais dans un cas j'ai une erreur et dans l'autre il ne trouve rien.

 

Quel est le problème ?

Qu'est ce que je fais mal ?

Comment régler cela ?

 

Merci de vos z'avis z'avisés.

 

 

4 RÉPONSES 4
Message 2 sur 5
AubelecBE
en réponse à: Jean-Marc68

sur ce site tu auras la fonction de recherche selon plusieures critères :

http://through-the-interface.typepad.com/through_the_interface/2008/07/conditional-sel.html

 

et celui la pour le cross..

http://through-the-interface.typepad.com/through_the_interface/2006/11/two_methods_for.html

 

 

 

pour le crossing c'est normal qu'il ne le trouve pas.

il faut que le carré formé par les 2 points croise le texte que tu cherches. mais là tu as donné 2 point trop proche du pt d'insertion de la croix.

De plus à mon avis il te faut plutot faire une recherche de tous les textes, et apres tu parcours les textes un à un et tu compares les points. Il faut faire attention que tu n'auras pas toujours les coordonnées identiques à chaque fois. car exemple :

pt bloc (x/y/z):  10.01,10.01,0

pt txt     : 10.011,10.011,0

--> si tu as définis les unité au format 0.00 --> tu auras un souci car le 10.011 sera pris pour un 10.01.

et apres tu as aussi à gérer les échelles si tu les utilises.

 

je travaille sous autocad 2010 mais je ne connais pas le MAP.

 

a+

Message 3 sur 5
_gile
en réponse à: Jean-Marc68

Salut,

 

Souvent, plutôt qu'un SelectAll(), je préfère scanner l'espace courant en utilisant une requête Linq pour filtrer plus facilement (et parfois plus finement).

 

Exemple pour trouver les textes par un point (méthode à appeler depuis une transaction) :

 

 

        private DBText[] GetTextByPosition(Point3d txtInsPt, Database db)
        {
            Transaction tr = db.TransactionManager.TopTransaction;
            if (tr == null)
                throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NotTopTransaction);
            RXClass textClass = RXClass.GetClass(typeof(DBText));
            BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead);
            return btr
                .Cast<ObjectId>()
                .Where(id => id.ObjectClass == textClass)
                .Select(id => (DBText)tr.GetObject(id, OpenMode.ForRead))
                .Where(txt => txt.Position.IsEqualTo(txtInsPt))
                .ToArray();
        }

 

Je ne suis pas certain de bien comprendre le contexte dans lequel sont recherché les textes, mais on peut optimiser la méthode ci-dessus en ne parcourant qu'une fois l'espace courant pour récupérer tous les paires de textes ayant le même point d'insertion dans une collection (ici un array mais ça pourrait être une liste) de IGrouping<Point3d, DBText>. Tableau dans lequel il sera plus rapide de faire des requêtes pour touver les paires de textes à partie d'un point (clé du IGrouping).

 

 

        private IGrouping<Point3d, DBText>[] GetTextPairs(Database db)
        {
            Transaction tr = db.TransactionManager.TopTransaction;
            if (tr == null)
                throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NotTopTransaction);
            RXClass textClass = RXClass.GetClass(typeof(DBText));
            BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead);
            return btr
                .Cast<ObjectId>()
                .Where(id => id.ObjectClass == textClass)
                .Select(id => (DBText)tr.GetObject(id, OpenMode.ForRead))
                .GroupBy(txt => txt.Position)
                .Where(grp => grp.Count() == 2)
                .ToArray();
        }

        private DBText[] GetTextByPosition(Point3d txtInsPt, IGrouping<Point3d, DBText>[] textPairs)
        {
            var pairs = textPairs.Where(p => p.Key.IsEqualTo(txtInsPt));
            if (pairs.Any())
                return pairs.First().ToArray();
            return null;
        }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 4 sur 5
_gile
en réponse à: _gile

Une autre façon de filtrer un jeu de sélection est de gérer l'évènement Editor.SelectionAdded.

 

Dans cet exemple, un champ privé est utilisé pour passer la valeur du point de la méthode GetTextByPoint() au getsionnaire d'évènnement.

Dans le gestionnaire d'évènnement, la méthode dite "obsolète" Open() est utilisé (considérons que c'est 'For advanced used') pour permettre un code plus concis, et s'affranchir de l'utilisarion d'une Transaction.

 

        private Point3d _txtInsPt;

        private PromptSelectionResult GetTextByPosition(Point3d txtInsPt, Editor ed)
        {
            _txtInsPt = txtInsPt;
            SelectionFilter filter = new SelectionFilter(new TypedValue[] { new TypedValue(0, "TEXT") });
            ed.SelectionAdded += OnSelectionAdded;
            PromptSelectionResult psr = ed.SelectAll(filter);
            ed.SelectionAdded -= OnSelectionAdded;
            return psr;
        }

        void OnSelectionAdded(object sender, SelectionAddedEventArgs e)
        {
            SelectionSet ss = e.AddedObjects;
            for (int i = 0; i < ss.Count; i++)
            {
                using (DBText txt = (DBText)ss[i].ObjectId.Open(OpenMode.ForRead))
                {
                    if (!txt.Position.IsEqualTo(_txtInsPt))
                    {
                        e.Remove(i);
                    }
                }
            }
        }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 5 sur 5
Jean-Marc68
en réponse à: _gile

Merci beaucoup de ton aide _gile

Tes explications et de l'info que j'ai trouvée ailleurs sur le net m'ont permi de me rendre compte que je n'étais pas si loin de la solution.
En fait ce qui boguait était la partie du filtre qui était sensée me permettre de définir le point d'insertion du texte à rechercher.

Il me suffisait donc de remplacer le
    acTypValAr.SetValue(new TypedValue((int)DxfCode.XCoordinate, txtInsPoint.X), 1);
    acTypValAr.SetValue(new TypedValue((int)DxfCode.YCoordinate, txtInsPoint.Y), 2);
    acTypValAr.SetValue(new TypedValue((int)DxfCode.ZCoordinate, txtInsPoint.Z), 3);
de mon filtre par
    acTypValAr.SetValue(new TypedValue((int)DxfCode.XCoordinate, txtInsPoint), 1);

 

J'ai été un peu désarçonné par le terme DxfCode.XCoordinate. Vu qu'il y a X dedans, je pensais qu'il ne retournerais que le X du point et qu'il me faudrait donc passer par 3 lignes. Mais en réalité le DxfCode.XCoordinate est le DXFCode des coordonnées complètes du point de départ d'un objet.


La solution que j'ai adoptée est donc la suivante

private SelectionSet GetTxtByPosition(Point3d txtInsPoint, Editor ed)
        {
            PromptSelectionResult ws = default(PromptSelectionResult);
            TypedValue[] acTypValAr = new TypedValue[2];
            acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "TEXT"), 0);
            acTypValAr.SetValue(new TypedValue((int)DxfCode.XCoordinate, txtInsPoint), 1);
            SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);
            ws = ed.SelectAll(acSelFtr);
            if (ws.Status == PromptStatus.OK)
            {
                return ws.Value;
            }
            return null;
        }

 Mais je garde ta solution parce qu'elle m'apprend beaucoup aussi et qu'il est toujours bon de pouvoir atteindre un but par différents moyens.

 

JM

Vous n'avez pas trouvé ce que vous recherchiez ? Posez une question à la communauté ou partagez vos connaissances.

Publier dans les forums