Getting/Setting Alignment Point Coordinates of Attributes Within Blocks

Getting/Setting Alignment Point Coordinates of Attributes Within Blocks

Grady_Groom
Explorer Explorer
682 Views
7 Replies
Message 1 of 8

Getting/Setting Alignment Point Coordinates of Attributes Within Blocks

Grady_Groom
Explorer
Explorer

Hi everyone!

 

I am working on a LISP routine to auto-align multiple attributes in a single row in a block, so that when a user has filled out the attributes, they can then run my LISP routine to auto-align the text so that it doesn't overlap. 

 

I have successfully created lists of the attributes I need and now am coding the alignment part.  Basically, I want to call JUSTIFYTEXT middle right on the leftmost attribute,  JUSTIFYTEXT middle left on the second attribute, get the Alignment X-coordinate of the leftmost attribute, set the Alignment X-coordinate of the second attribute equal to that of the leftmost attribute, and repeat this down the chain, setting the justifications for each attribute back to their original values when I'm finished.

 

However, the program gives me an ADS request error at the line below, where I try to grab the "AlignmentPoint/X" of an attribute so that I can manipulate it later. 

 

(setq coordNumber (getpropertyvalue (nth inc delimList) coordName))

 

I have verified through print statements that ...

 

-  coordName is the string "AlignmentPoint/X" (without the quotations)

 

- inc is 0

 

- The first value in delimList is the entity name of the attribute I want.  I verified this by calling getpropertyvalue on the entity name to see the Tag.

 

I then ran the dumpallproperties command on the entity name of the attribute, and I think my problem is that the AlignmentPoint values aren't reading through for some reason.  I pasted the whole property dump below if that helps, but the AlignmentPoint values all say "Failed to get value".  Can I not access the AlignmentPoint values if the attribute is within a block?  If so, I don't know if I can accomplish what I want to do.

 

Begin dumping object (class: AcDbAttribute)
AlignmentPoint/X (type: double) (RO) (LocalName: Text alignment X) = Failed to get value
AlignmentPoint/Y (type: double) (RO) (LocalName: Text alignment Y) = Failed to get value
AlignmentPoint/Z (type: double) (RO) (LocalName: Text alignment Z) = Failed to get value
Annotative (type: bool) (LocalName: Annotative) = 0
AnnotativeScale (type: AcString) (RO) (LocalName: Annotative scale) = Failed to get value
BlockId (type: AcDbObjectId) (RO) = 1cee5ec6900
CastShadows (type: bool) = 0
ClassName (type: AcString) (RO) =
CollisionType (type: AcDb::CollisionType) (RO) = 1
Color (type: AcCmColor) (LocalName: Color) = BYLAYER
ExtensionDictionary (type: AcDbObjectId) (RO) = 0
FieldLength (type: int) = 0
FlowDirection (type: AcDbMText::FlowDirection) (RO) (LocalName: Direction) = 5
Handle (type: AcDbHandle) (RO) = 1a138
HasFields (type: bool) (RO) = 0
HasSaveVersionOverride (type: bool) = 0
Height (type: double) (LocalName: Height) = 0.200000
HorizontalMode (type: AcDb::TextHorzMode) = 1
Hyperlinks (type: AcDbHyperlink*)
Invisible (type: bool) (RO) (LocalName: Invisible) = 0
IsA (type: AcRxClass*) (RO) = AcDbAttribute
IsAProxy (type: bool) (RO) = 0
IsCancelling (type: bool) (RO) = 0
IsDefaultAlignment (type: bool) (RO) = 0
IsEraseStatusToggled (type: bool) (RO) = 0
IsErased (type: bool) (RO) = 0
IsMTextAttribute (type: bool) (RO) (LocalName: Multiple lines) = 0
IsMirroredInX (type: bool) (LocalName: Backward) = 0
IsMirroredInY (type: bool) (LocalName: Upside down) = 0
IsModified (type: bool) (RO) = 0
IsModifiedGraphics (type: bool) (RO) = 0
IsModifiedXData (type: bool) (RO) = 0
IsNewObject (type: bool) (RO) = 0
IsNotifyEnabled (type: bool) (RO) = 0
IsNotifying (type: bool) (RO) = 0
IsObjectIdsInFlux (type: bool) (RO) = 0
IsPersistent (type: bool) (RO) = 1
IsPlanar (type: bool) (RO) = 1
IsPreset (type: bool) (RO) = 0
IsReadEnabled (type: bool) (RO) = 1
IsReallyClosing (type: bool) (RO) = 1
IsTransactionResident (type: bool) (RO) = 0
IsUndoing (type: bool) (RO) = 0
IsVerifiable (type: bool) (RO) = 0
IsWriteEnabled (type: bool) (RO) = 0
Justify (type: AcDbText::AcTextAlignment) (LocalName: Justify) = 10
LayerId (type: AcDbObjectId) (LocalName: Layer) = 1cee5eb7d50
LineWeight (type: AcDb::LineWeight) (LocalName: Lineweight) = -1
LinetypeId (type: AcDbObjectId) (LocalName: Linetype) = 1cee5ec6a10
LinetypeScale (type: double) (LocalName: Linetype scale) = 1.000000
LocalizedName (type: AcString) (RO) = Attribute
LockPositionInBlock (type: bool) (RO) (LocalName: Lock position) = 0
MTextJustify (type: AcDbAttDef::MTextJustity) (LocalName: Justify) = Failed to get value
MatchOrientationToLayout (type: bool) (LocalName: Match orientation to layout) = Failed to get value
MaterialId (type: AcDbObjectId) (LocalName: Material) = Failed to get value
MergeStyle (type: AcDb::DuplicateRecordCloning) (RO) = 1
ModelTextHeight (type: double) (LocalName: Model text height) = Failed to get value
Normal/X (type: double) = 0.000000
Normal/Y (type: double) = 0.000000
Normal/Z (type: double) = 1.000000
ObjectId (type: AcDbObjectId) (RO) = 1cee5eabf00
Oblique (type: double) (LocalName: Obliquing) = 0.000000
OwnerId (type: AcDbObjectId) (RO) = 1cee5eab9f0
PaperTextHeight (type: double) (LocalName: Paper text height) = Failed to get value
PlotStyleName (type: AcString) (RO) (LocalName: Plot style) = ByColor
Position/X (type: double) (RO) (LocalName: Position X) = Failed to get value
Position/Y (type: double) (RO) (LocalName: Position Y) = Failed to get value
Position/Z (type: double) (RO) (LocalName: Position Z) = Failed to get value
ReceiveShadows (type: bool) = 0
Rotation (type: double) (LocalName: Rotation) = 0.000000
ShadowDisplay (type: AcDb::ShadowFlags) (RO) (LocalName: Shadow Display) = Failed to get value
Tag (type: AcString) (RO) (LocalName: Tag) = DELIM20
TextString (type: AcString) = -
TextStyleId (type: AcDbObjectId) (LocalName: Style) = 1cee5ec0ea0
Thickness (type: double) (LocalName: Thickness) = Failed to get value
Transparency (type: AcCmTransparency) (LocalName: Transparency) = Failed to get value
Value (type: AcString) (LocalName: Value) = -
VerticalMode (type: AcDb::TextVertMode) = 2
Visible (type: AcDb::Visibility) = 0
Width (type: double) (RO) (LocalName: Multiline text width) = 0.000000
WidthFactor (type: double) (LocalName: Width factor) = 1.000000
End object dump

0 Likes
Accepted solutions (2)
683 Views
7 Replies
Replies (7)
Message 2 of 8

Sea-Haven
Mentor
Mentor
Accepted solution

Ok this may help, you need to wrap it for all attributes, what it is doing is getting a bounding box of an attribute. 

(setq obj (vlax-ename->vla-object (car (entsel "\nPick block object "))))
(setq atts (vlax-invoke obj 'Getattributes))
; do loop etc
(setq att (nth x atts))
(setq blk (handent (vlax-get att 'handle)))
(vla-GetBoundingBox blk 'minpoint 'maxpoint)
(setq pointmin (vlax-safearray->list minpoint))
(setq pointmax (vlax-safearray->list maxpoint))
; can now get X&Y as well of length of attribute. throw into alist sort on  say X then move each attribute accoridingly in that single block.

  Need a sample dwg to test properly.

0 Likes
Message 3 of 8

Grady_Groom
Explorer
Explorer

Thanks for the quick reply!  I am working on the top block in the attached drawing (AutoCAD Electrical, though I don't think that matters here) and will hopefully have time to test this tonight or tomorrow.  However, see an update below ...

 

I realized last night that I might be trying to do the wrong thing by moving the attributes themselves, because I will have multiple of this same block on one drawing, each with have different attribute values.  What I think I actually want to do is the equivalent of the screenshot below: select the block, grab the grip points of each attribute, and **STRETCH** the attributes to move them so that they don't overlap.  This doesn't appear to affect the position of the attributes in the block itself when I explode the block. 

 

In the screenshot below, it would mean keeping the "20" where it is and adjusting the other four overlapping attributes so that they align and don't overlap, like I did for the attributes in line "1".  Obviously, I can space out the attributes better in the block, but the WIRENO attributes in particular will have widely varying lengths so the alignment will never be quite right on its own.  And I don't want the user to have to go down each line grabbing grip points because there will be a lot of these blocks in the design, so I was hoping to find a way to do this in LISP.

 

Grady_Groom_1-1746973988731.png

 

 

0 Likes
Message 4 of 8

Sea-Haven
Mentor
Mentor

Ok ran into a little problem, the tag SEPARxx is at the end of the attribute definition, rather than being in the order viewed. It may have been an oversight when making the block. It adds a big bit of overhead into the get all the values. So I was going along real fast have tagname, textstring, physical length of tetxstring and so on, will have to think about a different way to get values. Just for future reference when playing with large amounts of attributes the order of the attributes can be quite critical to make lisp tasks simple. You could just reorder the attributes using Bedit and Battman which has a push attribute up in list.

SeaHaven_0-1747025831562.png

What is needed is a lisp put attribute 32 to position 4 and move rest down. I was at one stage looking at whether can do a vlax-put blk 'attributes "new order of attributes" may have another go at that.

 

 

0 Likes
Message 5 of 8

Grady_Groom
Explorer
Explorer

Hi Sea-Haven, I actually have that portion of the code working if you just want to use that!  I'm sorry I didn't think to attach it either.  The attached code will cycle through the attributes in the block and sort the attribute entity names into five separate lists for the five attributes in each line that I want to align - TERMXX, WIRENOXX, WIRENOXXA, SEPARXX, and DELIMXX.  Additionally, the entity names should be sorted in the same order in every list - the entity names for attributes TERM01, SEPAR01, WIRENO01, WIRENO01A, and DELIM01 should all fall in the same index position in their respective TERM, SEPAR, WIRENO, WIRENOA, and DELIM lists.

 

I left print statements in the code that I used to cycle through the lists after they were built, to display the tags for each attribute entity name, to show me that attributes in the same line of the block fell in the same spot in every list as I had hoped.  

 

I then hoped to use a simple while loop to adjust each line in the block one at a time, by justifying the TERM attribute middle right, adjusting the DELIM attribute middle left, setting appropriate DELIM alignment coordinate equal to the appropriate TERM alignment coordinate, changing the justification on the first two attributes back, etc.  I left this portion of the code commented out, along with a comment above the line that is failing because AutoCAD won't report the alignment coordinates of the TERM attribute. 

 

Edit:  Corrected and reattached the LISP file.

0 Likes
Message 6 of 8

Sea-Haven
Mentor
Mentor
Accepted solution

Ok if you can get the entity name of the attribute you can also use that to get the bounding box 

 

(setq obj2 (vlax-ename->vla-object  (handent (vlax-get att 'Handle))))
(vla-GetBoundingBox obj2 'minpoint 'maxpoint)
(setq pointmin (vlax-safearray->list minpoint))
(setq pointmax (vlax-safearray->list maxpoint))
(setq xd (- (car pointmax)(car pointmin)))

 and in turn the actual length of the string. 

 

I was reading the first 4 attributes and making a list, but the SEPAR was way down in the order hence suggesting reorder the block. I ended up with a list of.

 

(( (list tagname inspt xd txt)... ))4 times as in a row so a (car of the list shows all 4 values but need SEPAR as well. I was then going to move the attributes so they don't overlap.

 

I am going to look at a way of moving attributes up or down in order when you have like you have way to many to do manually. need to move a SEPAR like 30 up.

 

0 Likes
Message 7 of 8

Sea-Haven
Mentor
Mentor

Ok took a new approach and found something I was looking for. If you run this code on your sample dwg you will reorder the attributes list, it will not change there   display position but puts the SEPARX atts in correct location when filling details.

 

From a coding perspective makes your task so much easier as atts are in order. 

 

Can you please try it and comment back if ok to use the new dwg.

;;;	Original code by pBe May 2020   			;;;
; Modified by AlanH May 2025

(defun  c:changedisplayorder ( / ReorderMyAttributes thisTAg a )
(setq bn (vlax-get (vlax-ename->vla-object (car (entsel "\nPick block object "))) 'effectivename))
(defun ReorderMyAttributes (bn  / all blkn firstThree Therest thisTAg)
(setq thisTAg '("TERM01" "WIRENO01" "SEPAR01" "WIRENO01A" "DELIM01" "TERM02" "WIRENO02" "SEPAR02" "WIRENO02A" "DELIM02" 
"TERM03" "WIRENO03" "SEPAR03" "WIRENO03A" "DELIM03" "TERM04" "WIRENO04" "SEPAR04" "WIRENO04A" "DELIM04" 
"TERM05" "WIRENO05" "SEPAR05" "WIRENO05A" "DELIM05" "TERM06" "WIRENO06" "SEPAR06" "WIRENO06A" "DELIM06" 
"TERM07" "WIRENO07" "SEPAR07" "WIRENO07A" "DELIM07" "TERM08" "WIRENO08" "SEPAR08" "WIRENO08A" "DELIM08" 
"TERM09" "WIRENO09" "SEPAR09" "WIRENO09A" "DELIM09" "TERM10" "WIRENO10" "SEPAR10" "WIRENO10A" "DELIM10" 
"TERM11" "WIRENO11" "SEPAR11" "WIRENO11A" "DELIM11" "TERM12" "WIRENO12" "SEPAR12" "WIRENO12A" "DELIM12" 
"TERM13" "WIRENO13" "SEPAR13" "WIRENO13A" "DELIM13" "TERM14" "WIRENO14" "SEPAR14" "WIRENO14A" "DELIM14" 
"TERM15" "WIRENO15" "SEPAR15" "WIRENO15A" "DELIM15" "TERM16" "WIRENO16" "SEPAR16" "WIRENO16A" "DELIM16" 
"TERM17" "WIRENO17" "SEPAR17" "WIRENO17A" "DELIM17" "TERM18" "WIRENO18" "SEPAR18" "WIRENO18A" "DELIM18" 
"TERM19" "WIRENO19" "SEPAR19" "WIRENO19A" "DELIM19" "TERM20" "WIRENO20" "SEPAR20" "WIRENO20A" "DELIM20" 
"P_TAG1" "MFG" "CAT" "ASSYCODE" "DESC1" "DESC2" "DESC3" "INST" "LOC" "WD_WEBLINK" "WDBLKNAM" "FP" "P_ITEM" 
"DREF" "DREF01" "DREF02" "FORMAT_DREF" "FORMAT_DREF01" "FORMAT_DREF02" "MOUNT" "REFONLYLINKGUID" "SURF_DSP" 
"U_P_TAG1" "WD_WEBLINK1" "WD_WEBLINK2" "RATING1" "RATING10" "RATING11" "RATING12" "RATING2" "RATING3" 
"RATING4" "RATING5" "RATING6" "RATING7" "RATING8" "RATING9")
)
  (vlax-for itm (setq blkn (vla-item (vla-get-blocks
				       (vla-get-ActiveDocument
					 (vlax-get-acad-object)
				       )
				     )
				     bn
			   )
		)
	  (If (eq (vla-get-ObjectName itm) "AcDbAttributeDefinition")
	      (setq all	(cons (list
				(mapcar '(lambda (x)
					 (vlax-get-property itm x)
				       )
				      '("Tagstring"
					"TextString"
					"Height"
					"Mode"
					"PromptString"
					"StyleName"
					"Layer"
					"InsertionPoint"
				       )
			      )
				itm)
			    all
			)
	      	   )
	      )
     )
    (if
	  (vl-some '(lambda (ad) (if (member (caar ad) thisTAg) ad)) all)
	  (progn
	    (setq all (mapcar (function (lambda (atb)
					  (vla-delete (cadr atb)) (car atb))) all))	       
	    (setq firstThree (mapcar (function (lambda (tn)(assoc tn all))) thisTAg))
      	    (setq Therest
	       		(vl-remove-if (function (lambda (tn)
				       (member (car tn) thisTAg))) all))
	      (mapcar
		    (function
			      (lambda (lst)
				(setq nat (vla-AddAttribute blkn
					    (caddr lst)
					    (nth 3 lst)
					    (nth 4 lst)
					    (last lst)
					    (car lst)
					    (cadr lst)
					  )
				)
				(vlax-put nat 'StyleName (nth 5 lst))
				(vlax-put nat 'Layer (nth 6 lst))
			      )
			    )
		    (append firstThree theRest)
		  )
	      (princ (strcat "\nBlock \"" bn "\""))
	      (command "_.attsync" "Name" bn)
	      )
	     )
  		(princ)
	    )
	(While (setq a (tblnext "BLOCK" (null a)))
  		(if (= (cdr (Assoc 70 a)) 2)
		  	(ReorderMyAttributes (cdr (Assoc 2 a))))
)
  (princ)
)
(c:changedisplayorder)

 

Big thanks to @pbe 

 

 

0 Likes
Message 8 of 8

Grady_Groom
Explorer
Explorer

Good news!  I finally had the chance to try out the VLA functions you recommended in your first response, and I now have a routine that works exactly the way I hoped.  The values of the attributes shift to align themselves based on how long each string is, without overlaps or gaps, while the attributes themselves stay where they are inside the block.  I ended up using the bounding box function you recommended, and it worked great.  Your third post was also extremely helpful because I was able to take the attribute entity names I had already sorted through and just convert them to VLA objects before the part of the code that shifted the attributes.  I will mark both as solutions.

 

Because I was able to use the entity name sorting function I had already written, I didn't end up needing your last routine to shift the attribute order within the block.  I did test it out in AutoCAD and it worked as you said, although it also justified all of the attributes "Left" and I couldn't immediately see why.  When I had two of the same blocks within the drawing, the routine changed both blocks.  The routine did not work in AutoCAD Electrical, which is the program I am working with at the moment.  It threw a "consp nil" error, which is supposed to happen when you try to move a cable marker you shouldn't, and I couldn't tell why it threw the error in this case.  Regardless, I do not need the attribute reordering routine right now so no need to spend any more time on it, but I will revisit your routine in the future if I do.

 

Thank you for all your help and time!  

0 Likes