Extracting insertion point of invisible attributes in a block

Extracting insertion point of invisible attributes in a block

cdmiske
Enthusiast Enthusiast
2,970 Views
20 Replies
Message 1 of 21

Extracting insertion point of invisible attributes in a block

cdmiske
Enthusiast
Enthusiast

Hello all. I am looking around the interwebs to find out how to extract the insertion point of a n invisible attribute in a block. So far I have not had much luck. I am assuming it can be done with dotted pairs but need to know what I am looking for in order to make that happen. If anyone has any idea or info on how I might be able to do that would be greatly appreciated. Thank you in advance.

0 Likes
Accepted solutions (2)
2,971 Views
20 Replies
Replies (20)
Message 2 of 21

pbejse
Mentor
Mentor
Accepted solution

@cdmiske wrote:

Hello all. I am looking around the interwebs to find out how to extract the insertion point of a n invisible attribute in a block. So far I have not had much luck. I am assuming it can be done with dotted pairs but need to know what I am looking for in order to make that happen. If anyone has any idea or info on how I might be able to do that would be greatly appreciated. Thank you in advance.


Check for property "Invisible" , if true or -1 , extract property Insertion point.

 

 

(defun c:demo2 ( / blockwithattribute)
(if (setq blockwithattribute (ssget "_+.:S:E" '((0 . "INSERT")(66 . 1))))
(foreach itm 
	(vlax-invoke
	      (vlax-ename->vla-object (ssname blockwithattribute 0))
              				'GetAttributes)
	(if (minusp (vlax-get itm 'Invisible))
	  (progn(princ (Strcat "\nTag " (vla-get-tagstring itm)))
	  	(print (Vlax-get itm 'InsertionPoint))))))
(princ)

)

 

HTH

 

(defun c:demo ( / blockwithattribute n e )

(if (setq blockwithattribute (ssget "_+.:S:E" '((0 . "INSERT") (66 . 1))))
 (progn
   (setq n (entnext (ssname blockwithattribute 0)))
     (while (not (eq (cdr (assoc 0 (setq e (entget n)))) "SEQEND"))
       (if (and (not (eq (cdr (assoc 1 e)) nil))
                (= (cdr (assoc 70 e)) 1))
           (progn
                (princ (Strcat "\nTag " )) (Cdr (assoc 2 e))
		(print (cdr (assoc 10 e))))
           )
       (setq n (entnext n))
     )
    )
    )
   (princ)
 )

 

 

Message 3 of 21

hak_vz
Advisor
Advisor
Accepted solution
(defun c:attr_ins (/ e ent p lst)
(setq e (car (entsel "\nSelect block >")))
(while (setq e (entnext e))
(setq ent (entget e))
(setq p (cdr(assoc 0 ent)))
(if (and (eq p "ATTRIB") (=  (cdr (assoc 70 ent)) 1))(setq lst (cons (list (cdr(assoc 2  ent)) (cdr(assoc 10 ent))) lst)))
)
(reverse lst)
)

Miljenko Hatlak

EESignature

Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
Message 4 of 21

cdmiske
Enthusiast
Enthusiast

@pbejse 

 

Greatly appreciated! I can see I still have much to learn on how to extract data from objects in autolisp. At least I was heading in the right direction with (0, "insert") just had quite a bit more to learn in order to accomplish what I was needing.

0 Likes
Message 5 of 21

pbejse
Mentor
Mentor

@cdmiske wrote:

Greatly appreciated! I can see I still have much to learn on how to extract data from objects in autolisp. At least I was heading in the right direction with (0, "insert") just had quite a bit more to learn in order to accomplish what I was needing.


Good for you @cdmiske , just keep at it. you'll get there, we all have to start somewhere 😊

And you are welcome

 

0 Likes
Message 6 of 21

cdmiske
Enthusiast
Enthusiast

@hak_vz 

 

Thank you! This also is extremely helpful. The objective in using this info is to create an insertion point for a mleader to label blocks in a selection.

Message 7 of 21

Kent1Cooper
Consultant
Consultant

@cdmiske wrote:

.... The objective in using this info is to create an insertion point for a mleader to label blocks in a selection.


Then I think you're going to have to do some further conversion to make use of the information.  The positional data stored for an Attribute [or anything else] in a Block definition is relative to the Block's defined insertion point as virtual 0,0 location, not in terms of coordinates in the drawing that Block is inserted in.  It's easy enough to add that information to the Block's in-the-drawing insertion point, provided  the Block is at scale factors of +1 and rotation of 0.  It gets far more complicated if other scales and/or rotations are possible.

Kent Cooper, AIA
Message 8 of 21

cdmiske
Enthusiast
Enthusiast

@Kent1Cooper 

 

Thank you. That is good to know going forward. Sometimes I wish there was a very simple and direct explanation for development in autolisp when these kinds of issues arise. Part of the learning process is failure until you can get it right. There is always another step needed or hurdle to overcome in programming. I'll continue to dig into this further as I am working out each step of the lisp routine.

0 Likes
Message 9 of 21

cdmiske
Enthusiast
Enthusiast

@pbejse 

 

I have a question, as it is not showing up easily in google searches. Per @Kent1Cooper suggestion that the insertion point of the attribute is relative to the Block's defined insertion point I am doing some digging. I refereed back to an article i read last year "Mysteries of Autodesk's caves" that has some really fantastic information to help figure out how and where to extract dotted pair info, including the (0. "insert") and (66 . 1) along with how they relate to each other. What I am trying to figure out is the piece after the ssget, "_+.:S:E". I am curious to know if that is another dotted pair call and also want to know what is it referring to? Thank you for all your help so far.

0 Likes
Message 10 of 21

Kent1Cooper
Consultant
Consultant

@cdmiske wrote:

.... What I am trying to figure out is the piece after the ssget, "_+.:S:E". I am curious to know if that is another dotted pair call and also want to know what is it referring to? ....


That's making (ssget)'s selection emulate (entsel)'s, limited to one object by point pick.  See Lee Mac's page about (ssget) modes and options, some of which are unaccountably undocumented in the AutoLisp Reference.

Kent Cooper, AIA
Message 11 of 21

pbejse
Mentor
Mentor

@cdmiske wrote:

I have a question, as it is not showing up easily in google searches. Per @Kent1Cooper suggestion that the insertion point of the attribute is relative to the Block's defined insertion point ..


Yup, leave it to kent to find small things that get past us mere mortals. Need to look into that really.

 


@cdmiske wrote:

"Mysteries of Autodesk's caves" ... 


Me likey 🙂

 


@cdmiske wrote:

What I am trying to figure out is the piece after the ssget, "_+.:S:E". I am curious to know if that is another dotted pair call and also want to know what is it referring to? Thank you for all your help so far.


Want to know more about ssget filters and their usage? look no further ->> SSGET Function Format by Lee Mac 

You will find all that under Filter List Operators. 

 

Its great to know that you're keen on learning all that by researching first then ask later , kudos to you @cdmiske 

👍

 

EDIT: Kent posted the same link, fast draw kent is. [ Yoda ]

 

 

 

Message 12 of 21

cdmiske
Enthusiast
Enthusiast

Ok, i have it working in this current form:

 

(defun c:Note ( / obj ent items counter blkname attname attdxf p1 p2 a b c inspnt itm)
  (setq p1 '(0,0,0))
  (setvar "clayer" "A-Anno-Note")
  (command "cannoscale" "1/4\" = 1'-0\"")
  ;(setq leaderstyle (getvar "mleaderstyle"))
  ;(command _.mleaderstyle "Block (Straight Leader)" "")
  (setq ent (SSget))
  (if ent
      (progn
        (setq items (sslength ent ) )
        (setq counter -1 )
        (repeat items
          (setq counter (1+ counter ) )
          (setq blkname (ssname ent counter ) )
          (setq attname (entnext blkname ) ) 
          (eq "AcDbAttribute" (setq obj (vlax-ename->vla-object attname)))
	  ;(setq p1 (getpoint "\nSpecify arrowhead location: "))
	  (if (setq inspnt
                     (ssget "_+.:S:E"
                            '((0 . "INSERT")
                              (66 . 1))))
            (progn
                  (foreach
                         itm
                         (vlax-invoke
                               (vlax-ename->vla-object
                                     (ssname inspnt 0))
                               'GetAttributes)
                        ;(if (minusp (vlax-get itm 'Invisible))
                              (progn
                                    (setq p1 (Vlax-get
                                                 itm
                                                 'InsertionPoint))))))
            ;)
	  ;(princ p1)

	  (setq a (+ (car p1) 12))
	  (setq b (+ (cadr p1) 12))
	  (setq c (caddr p1))
	  (setq p2 (list a b c))

	  (princ p1)
	  (princ p2)
	  (princ a)
	  (princ b)
	  (princ c)
	  
	  ;(setq p2 (getpoint "\nSpecify leader landing location: "))
	  (while attname
            (setq attdxf (entget attname ) )
            (if
		(= (cdr (assoc 2 attdxf )) "ACCESS")
	      
		(command "mleader" p1 p2 
      (strcat "%<\\AcObjProp Object(%<\\_ObjId " (itoa (vla-get-objectid obj)) ">%).TextString>%") ) )
            (if (= (cdr (assoc 0 (entget attname ))) "SEQEND" )
		(setq attname nil )
		(setq attname (entnext attname )) )
          )
          (setq attname blkname )
        ) 
      )
    )
  (setvar "clayer" "0")
  ;(command _.mleaderstyle leaderstyle)
  (princ)
)

 

I still have a few more things to figure out to make the routine more robust:

 

  1. I have some blocks with more than one hidden attribute and can add those here:
(while attname
            (setq attdxf (entget attname ) )
            (if
		(= (cdr (assoc 2 attdxf )) "ACCESS")

What I would like to do is make this cycle through each one individually probably with a either a foreach or a while. Also guessing I would need to move this part above the part where I pull the points so it can find the insertion point of each one.

     2. I would like to not have to not have to select each block a second time to have the points get calculated when this:

(if (setq inspnt
                     (ssget "_+.:S:E"
                            '((0 . "INSERT")
                              (66 . 1))))

Is ran so it automatically will cycle through the selection that happens  during this:

 (setq ent (SSget))
  (if ent
      (progn
        (setq items (sslength ent ) )

To make sure that the right leader is associated with the correct block.

 

I have included a .dwg with my test blocks for reference.

 

I am still reading and learning about how to assemble code in LISP to behave how I want it to behave so if I can get some direction or places to look would be greatly appreciated. I am digging through Lee Mac's site and Afralisp to see what I can find along with the forums both autodesk and cadTutor.

 

My last step will be to go back through to add in error handling.

 

0 Likes
Message 13 of 21

cdmiske
Enthusiast
Enthusiast

As I am looking into this I am guessing I could set a list to look for all the named attributes that are to be labeled and use a (repeat (sslength [list])) cycle to make sure that each attribute in the list is labeled contained within the block before cycling to the next block. 

 

I am also thinking that I would be better off using a foreach to cycle through each block or maybe for both the block and the attribute list. I still confuse myself on when to use foreach, while, and repeat. Is there a rule of thumb I can use for guidance?

 

 

0 Likes
Message 14 of 21

pbejse
Mentor
Mentor

@cdmiske wrote:

As I am looking into this I am guessing I could set a list to look for all the named attributes that are to be labeled and use a (repeat (sslength [list])) cycle to make sure that each attribute in the list is labeled contained within the block before cycling to the next block. 


Along with the previous post, its a lot to take in in one seating @cdmiske 

In a nutshell, what you're trying to achive is to label ALL parts of the block where an invisible attribue is found correct?

And you dont want to select the block the second time

(setq inspnt  (ssget "_+.:S:E" '((0 . "INSERT") (66 . 1))))

after the initial call to ssget... 

 (setq ent (SSget))

 

 I will make a similar one, and you can disect the code and learn from it.

Sounds go to you?

 

Message 15 of 21

cdmiske
Enthusiast
Enthusiast

@pbejse 

 

That sounds perfect! Thank you!

 

So far I have been able to get everything to work as I hoped to. I am figuring that the order of operations is need to be adjusted. So digging through tutorials and books along with trying to experiment on rearranging the code to see if I can make things work in the order I am thinking it need to flow.

 

I am learning quite a bit on this one and semi abandoned the other things I am working on as my position was eliminated working on developing restaurants. Luckily I was able to get a new gig working on VA hospitals and specifically developing tools improve performance and develop design standards / template systems to help normalize the department.

0 Likes
Message 16 of 21

pbejse
Mentor
Mentor

@cdmiske wrote:

So far I have been able to get everything to work as I hoped to. I am figuring that the order of operations is need to be adjusted. So digging through tutorials and books along with trying to experiment on rearranging the code to see if I can make things work in the order I am thinking it need to flow.


Here it is [ tested on your sample drawing ]

 

(defun c:demo (/ indexNumber SelectedBlocksWithAttribute
	       ObjectAtIndexPositiononSelectionSet
	       entityData )
  (if (setq indexNumber	0
	    ; 		this ensures the selection has attributes	;
	    SelectedBlocksWithAttribute (SSget '((0 . "INSERT") (66 . 1)))
      )									
    (repeat (sslength SelectedBlocksWithAttribute)
      (setq ObjectAtIndexPositiononSelectionSet
	     (ssname SelectedBlocksWithAttribute indexNumber)
      ) ;_ end of setq

      (while (setq ObjectAtIndexPositiononSelectionSet
		    (entnext ObjectAtIndexPositiononSelectionSet)
	     ) ;_ end of setq
	(setq entityData (entget ObjectAtIndexPositiononSelectionSet))
	(setq ObjectType (cdr (assoc 0 entityData)))
	;	 This line only include invisible attribites		;
	(if (and (eq ObjectType "ATTRIB")
		 (= (cdr (assoc 70 entityData)) 1)
	    ) ;_ end of and
	  (progn
	    (command "_Point" "_non" (cdr (assoc 10 entityData)))
	    (print (cdr (Assoc 1 entityData)))
	  ) ;_ end of progn

	) ;_ end of if
      ) ;_ end of while
      (setq indexNumber (1+ indexNumber))
    ) ;_ end of repeat
  ) ;_ end of if
  (princ)
) ;_ end of defun

 

 

Now if you want to target specific TAG names, add these lines

...
(Setq targetTags '("ACCESS" "OTHERTAGS")) (if (setq indexNumber 0 ; this ensures the selection has attributes ; SelectedBlocksWithAttribute (SSget '((0 . "INSERT") (66 . 1))) ) ... (and (eq ObjectType "ATTRIB") (= (cdr (assoc 70 entityData)) 1) (member (cdr (assoc 2 entityData)) targetTags) )

Hope this helps

 

Right now , all it does is place a point entity on the insertionpoint of the attribute and print the value on the commandline.

 

0 Likes
Message 17 of 21

pbejse
Mentor
Mentor

@cdmiske wrote:

I am learning quite a bit on this one and semi abandoned the other things I am working on as my position was eliminated working on developing restaurants. Luckily I was able to get a new gig working on VA hospitals and specifically developing tools improve performance and develop design standards / template systems to help normalize the department.


We are all affected by the pandemic one way or the another @cdmiske . A lot in our industry here was given a choice between  a salary cut or the highway.  Its good to know that you decided to learn all this on your own time. Kudos to you. 👍

 


@cdmiske wrote:

I am also thinking that I would be better off using a foreach to cycle through each block or maybe for both the block and the attribute list. I still confuse myself on when to use foreach,while, and repeat. Is there a rule of thumb I can use for guidance?


All three functions are equally fast.

  • Foreach process the items from a list a based on the its length
  • Repeat as the name implies evaluates expression by a specified number of times via int value

Both will run its course at an exact number of times specified by the supplied argument. With while on the other hand will continue to run an expression until the main condition evaluates to nil, you can terminate the loop at at given condition.

 

Functions Reference (AutoLISP) 

 

HTH

 

0 Likes
Message 18 of 21

cdmiske
Enthusiast
Enthusiast

@pbejse 

 

I got it working today and have been experimenting with trying to use the fields method. I am getting close to understanding how to pull each objectID for each of the attributes to utilize the fields method.

 

I do have a unique glitch happening. In the attached block if you run the lisp and only select the handicap toilet layout it will automatically add the separate grab bar when it cycles through the repeat. If you only select the single grab bar it will only run that instance. I am wondering if there is some bizarre relationship between the two from copying and pasting the attribute. Not complaining as it goes ahead and labels everything in my drawings at the moment without needing to window a whole group.

 

I have some cleanup to do along with error handling but at least I have it working.

 

Thanks for all the help @pbejse@Kent1Cooper, and @hak_vz !

0 Likes
Message 19 of 21

pbejse
Mentor
Mentor

@cdmiske wrote:

I do have a unique glitch happening. In the attached block if you run the lisp and only select the handicap


There is a glitch and it's not yoursbut mine. My bad, I should've picked up on that before i posted.

For that I apologize @cdmiske 

 

 

(defun c:note ( / indexNum obj ent items ent entityData )
  (setq targetTags '("ACCESS""ACCESS2""ACCESS3" "MARK"))    
  (if (setq indexNum	0	    
	    obj (SSget '((0 . "INSERT") (66 . 1)))
      )									
    (repeat (sslength obj) 
      (setq ent (ssname obj indexNum))      
      (while (and (setq ent (entnext ent))
		  (= "ATTRIB" (cdr (assoc 0 (setq entityData (entget ent))))))
	        (if
		  (and
		    (not (eq (cdr (assoc 1 entityData)) nil))
		    (= (cdr (assoc 70 entityData)) 1)
		    (member (cdr (assoc 2 entityData)) targetTags)
		  )
		   (progn
		     (print
		       (itoa (vla-get-objectid
			       (vlax-ename->vla-object
				 (entnext (cdr (assoc 330 entityData)))
			       )
			     )
		          )
		     )
		     (print (cdr (assoc 2 entityData)))
		     (command "mleader"
		       (cdr (assoc 10 entityData))
		       (list (+ (car (cdr (assoc 10 entityData))) 12)
			     (+ (cadr (cdr (assoc 10 entityData))) 12)
			     (caddr (cdr (assoc 10 entityData)))
		       )
		       (strcat (cdr (Assoc 1 entityData)))
		     )
		   )
		)
	)	      
      (setq indexNum (1+ indexNum))
    )  
  )  
  (princ)
)

 

HTH

 

0 Likes
Message 20 of 21

cdmiske
Enthusiast
Enthusiast

@pbejse 

 

Fantastic!! Everything works as I expect it too now.

 

I was able to figure out some way of adding each tag as a field into the leader. It might be a bit clunky, but it does work. I have attached the latest code in as well for anyone who might need to dissect how I was able to make it function. Also attached the reference drawing again to help those who can use this routine so that they can figure out how to make it work for them.

 

Now time to build an entire library of blocks for this function to work with.

 

Again thank you for all your help! This has been a really great learning experience for me.

0 Likes