Aller au contenu

Lister des blocs par le nom d'attributs


Messages recommandés

Posté(e)

Bonsoir,

 

Je voudrais rechercher tous les blocs ayant un attribut nommé MAT pour dresser une liste de points afin de connaitre le dernier matricule à utiliser, sans forcément connaitre les noms de blocs, je pensais utiliser un jeu de sélection :

 

(setq entl  (ssget "_X" '((0 . "ATTRIB") (2 . "MAT"))))

 

mais si je poste une question c'est que ça ne marche pas.

 

j'avais essayé des entget sur des blocs mais je n'arrive pas à comprendre l'enchassement des entités les unes dans les autres. Bref, je suis bloqué, si quelqu'un a une idée, j'ai trainé sur des blogs mais les routines de manipulation de blocs font souvent appel à du visual et je ne connais pas ces fonctions. Merci d'avance.

Geometre - Autocad 2016 - Covadis v17.0

Posté(e)

Salut,

On ne peut filtrer directement avec les attributs. Il faut traiter la sélection a posteriori.

;; sélectionner tous les blocs avec attribut
(setq s (ssget "_X" '((0 . "INSERT") (66 . 1))))
;; parcourir le jeu de sélection pour écarter le blocs n'ayant pas d'attribut "MAT
(repeat	(setq i (sslength s))
 (setq b (ssname s (setq i (1- i))))
 (if (vl-catch-all-error-p
(vl-catch-all-apply 'getpropertyvalue (list b "MAT"))
     )
   (ssdel b s)
 )
)

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

Posté(e)

Bonsoir,

 

Merci pour la routine, ca marche nickel, du coup j'ai ajouté une seconde boucle derrière pour rechercher le max des attributs et ça roule, ça m'a permis de découvrir le "getpropertyvalue" qui est terrible pour récupérer des infos.

 


;j'ai un jeu de sélection s avec tous les blocs dont l'attribut est MAT

;je dois rechercher les valeurs de ces attributs puis en chercher le max
(setq	i	0
 	maxlist	0)
(repeat (sslength 	s)
 	(setq		matr 	(atoi (getpropertyvalue (ssname s i) "MAT"));je récupère les valeurs des matricules
		i	(1+	i)
  		listmat	(cons	matr	listmat) ;je construis la liste des tous les matricules
)
 	(if	(> matr maxlist);je recherche la valeur max des attributs
  (setq	maxlist	matr)
  );if
);repeat


 

 

Salut,

On ne peut filtrer directement avec les attributs. Il faut traiter la sélection a posteriori.

;; sélectionner tous les blocs avec attribut
(setq s (ssget "_X" '((0 . "INSERT") (66 . 1))))
;; parcourir le jeu de sélection pour écarter le blocs n'ayant pas d'attribut "MAT
(repeat	(setq i (sslength s))
 (setq b (ssname s (setq i (1- i))))
 (if (vl-catch-all-error-p
(vl-catch-all-apply 'getpropertyvalue (list b "MAT"))
     )
   (ssdel b s)
 )
)

Geometre - Autocad 2016 - Covadis v17.0

Posté(e)

Bonsoir,

 

je reviens avec ma liste de points en fonction des attributs, une fois que j'ai la sélection des blocs avec un attribut MAT, je souhaite insérer un point sur chaque sommet mais avant je souhaite vérifier qu'il n'y a pas déja de points sur les sommets, donc à partir de la sélection s des points avec un attribut MAT, je la parcours pour vérifier que les sommets de la polyligne "listpts" n'existent pas et je veux supprimer ces points de "lispts" pour ensuite insérer les points uniquement sur les sommets restants.

 

je comprends pas ce qui bug dans mon traitement :

 

(repeat (sslength s)
 	(setq	coordbloc 	(list	(getpropertyvalue (ssname s ind1) "Position/X")	(getpropertyvalue (ssname s ind1) "Position/Y") ) ) ;je parcours les points avec MAT pour construire leurs coordonnées
 	(setq	ind1	(1+	ind1) )
(vl-remove	coordbloc	listpts )		;je supprime l'entité de la liste des sommets
);repeat

Geometre - Autocad 2016 - Covadis v17.0

Posté(e)

Coucou,

 

Premièrement, plutôt que de passer par (getpropertyvalue) pour récupérer les coordonnées X et Y du point d'insertion du bloc, je pense qu'il suffit de passer par un (cdr (assoc 10 (entget (ssname s ind1)))).

Ensuite, serait-il possible d'avoir un aperçu de la valeur de tes variables, listpts notamment. A quel endroit précisément as-tu une erreur ? Est-ce lors de la suppression des coordbloc de ta listpts ?

 

Bon après si je comprend bien, tu possèdes un plan topo avec les blocs TCPOINT (un point, un attribut ALT et un attribut MAT) et tu souhaites être en mesure de placer automatiquement ces blocs sur des polylignes existantes (issues d'un relevé) en incrémentant le matricule par rapport à la dernière valeur de matricule insérée et en plaçant ces blocs sur tous les sommets de polylignes (sauf s'il y a déjà un bloc à cet emplacement), c'est bien chat ?

Si c'est bien cela, alors comment définis-tu l'altitude (attribut ALT) lors de l'insertion ? S'agit-il de polylignes "LWPOLYLINE" ou bien "POLYLINE" 3D ?

 

Car en fonction du type de polyligne à lire, les sommets ne sont pas récupérables de la même manière..

 

Bisous,

Luna

Posté(e)

Bonjour,

 

Pour la routine c'est cela :

 

tu souhaites être en mesure de placer automatiquement ces blocs sur des polylignes existantes (issues d'un relevé) en incrémentant le matricule par rapport à la dernière valeur de matricule insérée et en plaçant ces blocs sur tous les sommets de polylignes (sauf s'il y a déjà un bloc à cet emplacement), c'est bien chat ?

 

sauf que la polyligne je la crée puisque c'est une reconstitution de contour et sur ce contour je veux insérer un bloc POINT à chaque sommet, sauf s'il en existe déjà un.

 

Pour l'attribut ALT, je ne m'en occupe pas, je travaille en 2D.

 

Je réalise la sélection de tous les blocs ayant un attribut MAT avec la routine de GILE

 

(setq 	s 	(ssget "_X" '((0 . "INSERT") (66 . 1))))
(repeat (setq i (sslength s))
 		(setq b (ssname s (setq i (1- i))))
 	(if 	(vl-catch-all-error-p
       	(vl-catch-all-apply 'getpropertyvalue (list b "MAT"))
     		)
   	(ssdel b s)
 	);if
);repeat

 

puis je liste les sommets de la polyligne sur lesquels je souhaite ajouter des blocs :

 

 


(setq 	ptinser		(getpoint "\nCliquez dans le contour à immatriculer:"))
(command-s	"-contour"	ptinser	"")
(setq nompoly		(cdr (assoc -1 (entget (entlast)))))

(setq	liste-sommets '()
       ent-liste (entget nompoly)
       n 0)
(repeat (length ent-liste)
  	(if (= 10 (car (nth n ent-liste)))
     	(setq liste-sommets (cons (cdr (nth n ent-liste)) liste-sommets))
   	);if 
	(setq n (1+ n))
);repeat
(setq liste-sommets (reverse liste-sommets))


 

et je veux ensuite réaliser ma routine de suppression de "liste-sommets" en enlevant les points du jeu de sélection "s" s'ils existent dans les deux.

"s" est un jeu de sélection de blocs

 

"liste-sommets" est une liste de type:

Commande: !liste-sommets

((935.077 180.848) (905.821 188.627) (888.46 170.264) (904.042 159.066) (924.398 148.397))

 

la polyligne est bien LWPOLYLINE

 

Espérant avoir été assez clair, peux tu m'expliquer s'il y a une différence à parcourir la liste DXF ou à utiliser "getpropertyvalue"?

Geometre - Autocad 2016 - Covadis v17.0

Posté(e)

Bonjour,

 

Gilles a écrit un tas de routine pour manipuler les listes (dernière ligne du 1er message) ICI

dont une fonction "exclusive" qui devrait faire le boulot

 

;; EXCLUSIVE
;; Retourne une liste contenant les éléments appartenant exclusivement à l1

(defun exclusive (l1 l2)
 (if l1
   (if	(member (car l1) l2)
     (exclusive (cdr l1) l2)
     (cons (car l1) (exclusive (cdr l1) l2))
   )
 )
)

 

 

Olivier

Posté(e)

Coucou,

 

Je me demande si...la raison ne vient pas plutôt du fait que l'on compare deux points (possédant chacun des coordonnées) et j'avais déjà eu un soucis similaire à cause notamment au géoréférencement de mes plans mais aussi parfois sur des écarts de précision...

 

J'avais essayé de solutionner le problème avec cette fonction mais je ne sais pas si cela te sera utile :

(defun pt-member (pt pt-list fuzz / d)

(setq d (strlen (substr (vl-princ-to-string fuzz) (1+ (cond ((= (type fuzz) 'REAL) (vl-string-position (ascii ".") (vl-princ-to-string fuzz))) (t 0))))))
(if (> (length pt) (length (car pt-list)))
	 (setq pt (reverse (cdr (reverse pt))))
)
(while (and pt-list
	    (not (equal (mapcar '(lambda (x) (rtos x 2 d)) pt) (mapcar '(lambda (x) (rtos x 2 d)) (car pt-list)) fuzz))
       )
	(setq pt-list (cdr pt-list))
)
pt-list

)

L'idée ici c'est de vérifier si oui ou non, un point appartient à une liste de point, avec pt le point à comparer, pt-list la liste de point à comparer et fuzz le degré de précision. Plus le fuzz est précis (ex : 0.001) et plus tu veux t'assurer de la position exacte du point. Le retour est similaire à la fonction (member) qui retourne la liste restante à partir du point correspondant à la recherche.

C'était une idée comme une autre, développé en peu de temps donc probablement pas parfaite mais cela avait corrigé mon souci à l'époque.

 

Il serait également possible de comparer les points via (distance), bref un tas d'améliorations ^^

 

Petite remarque, tu n'as pas besoin d'écrire

(setq nompoly (cdr (assoc -1 (entget (entlast)))))

étant donné que (entlast) te renvoie déjà le même résultat donc petit raccourci avec

(setq nompoly (entlast))

et un autre raccourci, c'est la fonction (member) pour récupérer la liste des sommets d'une polyligne, car tu ne t'intéresse qu'au code DXF 10 donc boucler un (member) en tronquant à chaque passage dans la boucle la liste obtenue via (member), c'est plus rapide à l'exécution.

 

Bisous,

Luna

Posté(e)

Bonjour,

 

Je n'ai pas réussi à utiliser ta routine, j'ai retravaillé de mon côté en construisant la liste des positions dans la liste que je parcours des points que je recherche en fonction d'une distance fixe, puis à partir de cette liste de positions je reconstruis ma liste de points en ayant exclu ceux qui sont à moins de 0.02m des existants. ca a l'air plus bourrin mais ca fonctionne, je te mets la routine :

 

 


;il faut que je compare la liste 's' des blocs dans le calque PIP-90 et la liste-sommets de la polyligne listpts
 (setq 	s 	(ssget "_X" '((0 . "INSERT") (8 . "PIP-90")))
ind1	0
entl	'()
listpt	'()
)

 (if	(/= nil s)
   (progn
 	(repeat	(sslength s)
	(setq	coordbloc	(list (car (cdr (assoc 10 (entget (ssname s ind1 ))))) (cadr (cdr (assoc 10 (entget (ssname s ind1))))))
		ind1	(1+ ind1)
		ind2	0
		);setq
	(repeat	(length	listpts)
		(if	(< (distance 	coordbloc 	(nth	ind2 	listpts) ) 	0.02) 	;je veux rechercher les points qui sont à moins de 2 cm des 'coordbloc' existants
	   		(setq	entl	(cons	ind2	entl))					;je construis la liste des indices de points communs pour ensuite les supprimer dans 'listpts'
		  );if
		(setq   ind2	(1+ ind2) )
	  );repeat
	);repeat
(setq	i	0
	n	0
	)
   	(repeat	(- (length	 listpts) 1 )
	;je veux construire la liste 'listpt' en insérant chacun des éléments de 'listpts' sauf ceux qui sont dans la liste 'entl'
	(if ( /= i (nth n entl) )
		 (setq listpt (cons (nth i listpts) listpt )
		      i	(1+ i)
		      ) )
	
	 (if ( = i (nth n entl))	
		(setq n	(1+ n)
		      i	(1+ i)
		      ) )
  );repeat
(setq listpts (reverse listpt ) )
     );progn	

   );if 

 

Je ne suis pas trop sur de la seconde boucle 'repeat' avec les deux 'if' qui se succèdent, j'avais essayé avec mapcar qui me semblait adapté mais je n'ai pas réussi à écrire quelquechose qui tourne.

Geometre - Autocad 2016 - Covadis v17.0

Posté(e)

Bonjour,

 

A partir des 2 fonctions de Giles : EXCLUSIVE et MEMBER-FUZZ on peut créer EXCLUSIVE-FUZZ qui permet de renvoyer une liste exclusive avec une tolérance

J'en ai profité pour mettre une fonction POINT2D qui renvoie un point 2D à partir d'un point 3D

;; MEMBER-FUZZ
;; Comme MEMBER avec une tolérance dans la comparaison

(defun member-fuzz (expr lst fuzz)
 (while (and lst (not (equal (car lst) expr fuzz)))
   (setq lst (cdr lst))
 )
 lst
)

;; EXCLUSIVE
;; Retourne une liste contenant les éléments appartenant exclusivement à l1

(defun exclusive (l1 l2)
 (if l1
   (if (member (car l1) l2)
     (exclusive (cdr l1) l2)
     (cons (car l1) (exclusive (cdr l1) l2))
   )
 )
)

;; EXCLUSIVE-FUZZ
;; Retourne une liste contenant les éléments appartenant exclusivement à l1 avec une tolérance de comparaison

(defun exclusive-fuzz (l1 l2 fuzz)
 (if l1
   (if (member-fuzz (car l1) l2 fuzz)
     (exclusive-fuzz (cdr l1) l2 fuzz)
     (cons (car l1) (exclusive-fuzz (cdr l1) l2 fuzz))
   )
 )
)

;; retourne un point 2D à partir d'une liste de 2 ou 3 coordonnées
(defun Point2D (pt)
 (list (car PT) (cadr PT))
)

 

 

A partir de là ta routine se simplifie pas mal

 


 ;il faut que je compare la liste 's' des blocs dans le calque PIP-90 et la liste-sommets de la polyligne listpts
 (setq s   	(ssget "_X" '((0 . "INSERT") (8 . "PIP-90")))
       ind1    0
       listpt  '()
dTolr   0.02
       )

 (if   s
   (progn
     (repeat (sslength s)
       (setq coordbloc (Point2D (cdr (assoc 10 (entget (ssname s ind1 )))))
             ind1      (1+ ind1)
       );setq
 	
  	(setq listpt (append (listpt (list coordbloc))))
     
     );repeat

     ;renvoie les éléments de listpts qui ne sont pas dans listpt à la tolérance près
     (setq listpts-restante (exclusive-fuzz listpts listpt dTolr))
     
   );progn   
       
 );if 

 

 

Je l'ai écrite à l'arrache sans tester donc code à vérifier

 

Olivier

Posté(e)

 



;; EXCLUSIVE-FUZZ
;; Retourne une liste contenant les éléments appartenant exclusivement à l1 avec une tolérance de comparaison

(defun exclusive-fuzz (l1 l2 fuzz)
 (if l1
   (if (member-fuzz (car l1) l2 fuzz)
     (exclusive-fuzz (cdr l1) l2 fuzz)
     (cons (car l1) (exclusive-fuzz (cdr l1) l2 fuzz))
   )
 )
)

 

 

Je l'ai écrite à l'arrache sans tester donc code à vérifier

 

Olivier

 

Bonjour,

 

Je te remercie pour ce retour, je vais tester ca, ca simplifie énormément, je débute, je patauge un peu.

 

Il y a un point que je ne comprends pas dans les fonctions et j'avais vu ca dans pas mal d'exemples de routines, quand tu définis la fonction entre des bornes 'defun', comment tu peux utiliser cette fonction 'dans elle même' en quelque sorte?

Geometre - Autocad 2016 - Covadis v17.0

Posté(e)

Coucou,

 

Concernant le (mapcar) pour la seconde boucle je pense que ceci correspond à ce que tu cherches à faire :

(setq listpt (vl-remove-if '(lambda (x) (member x entl)) listpts))

 

Bisous,

Luna

Posté(e)

Bonjour,

 

Je te remercie pour ce retour, je vais tester ca, ca simplifie énormément, je débute, je patauge un peu.

 

Il y a un point que je ne comprends pas dans les fonctions et j'avais vu ca dans pas mal d'exemples de routines, quand tu définis la fonction entre des bornes 'defun', comment tu peux utiliser cette fonction 'dans elle même' en quelque sorte?

 

Ca s'appelle la récursivité et ça ne pose pas de souci au niveau du langage (mais du cerveau oui!).

Il suffit d'une condition pour sortir de la boucle de récursivité.

 

l'exemple le plus couramment utilisé pour présenter ce concept de récursivité, c'est la fonction factorielle

n! = n * (n-1)! et 0! = 1 (condition de sortie pour arrêter l'empilage)

 

Olivier

Posté(e)

Salut,

 

Pour le fonctions récursives, tu peux voir la section 13.3 de Introduction à AutoLISP téléchargeable sur cette page.

 

Merci pour cet envoi, je vais regarder cela, tout un monde de fonctions qui s'ouvre à moi, la compréhension du fonctionnement n'est pas aisée pour un novice, le factoriel c'est tellement simple que ca en est un peu trivial alors que l'application aux autres fonctions est pas évidente.

Geometre - Autocad 2016 - Covadis v17.0

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é