Move Layers By X Distance

Move Layers By X Distance

LeoManu
Contributor Contributor
755 Views
8 Replies
Message 1 of 9

Move Layers By X Distance

LeoManu
Contributor
Contributor

Hello, I have a DWG file with a lot of pages stacked on top of each other, and I would like to separate them by a distance X to work with them more easily. Each page is on a different layer, so it's possible to move them by selecting the layer corresponding to each page. The title block is on layer 0 and is the same for all pages and should be copied to each of the existing pages. I have attached a DWG file with more details. I managed to make some progress with the following code, but I can't seem to sort them by name as they appear in the layer properties. I would like the pages to be moved in the correct order. Thank you very much.

 

 

(defun c:MoveLayersByXDist ()
  (prompt "\nEnter the 'X distance' value: ")
  (setq xDist (getreal))  ; Prompts the user to enter the X distance

  (if (not xDist)
    (progn
      (prompt "\nError: A numeric value is required for 'X distance'.")
      (exit)
    )
  )

  (setq layers (getLayers)  ; Get a list of all layers
        counter 1  ; Counter to multiply the distance
        selectionLayer0 (ssget "X" (list (cons 8 "0")))  ; Select all objects on layer "0"
        distances '())  ; List to store copy distances

  ; Move all layers except "0" and "1"
  (foreach layer layers
    (if (not (member layer '("0" "1")))  ; Ignore layers "0" and "1"
      (progn
        (setq selection (ssget "X" (list (cons 8 layer))))  ; Select all on the current layer
        (if selection
          (progn
            (setq distance (* counter xDist))  ; Calculate the move distance
            (command "_.move" selection "" "0,0,0" (strcat (rtos distance 2 6) ",0,0"))  ; Move the selection
            (setq distances (cons distance distances))  ; Store the copy distance
            (setq counter (1+ counter))  ; Increment the counter
          )
        )
      )
    )
  )

  ; Copy the contents of layer "0" according to the accumulated distances
  (if selectionLayer0
    (foreach distance (reverse distances)  ; Iterate over the distances in order
      (command "_.copy" selectionLayer0 "" "0,0,0" (strcat (rtos distance 2 6) ",0,0"))
    )
  )

  (princ "\nLayer movement and layer '0' copy process completed.")
  (princ)
)

(defun getLayers ()
  (setq layerList '())
  (vlax-for layer (vla-get-layers (vla-get-activedocument (vlax-get-acad-object)))
    (setq layerList (cons (vla-get-name layer) layerList))
  )
  (reverse layerList)
)

 

0 Likes
Accepted solutions (1)
756 Views
8 Replies
Replies (8)
Message 2 of 9

Sea-Haven
Mentor
Mentor

You seem to have 3 posts all asking for a solution to your dwg problem. Where you want layouts made by layer.

 

Create a New Layout for Each Existing Layer - Autodesk Community - AutoCAD

 

There is no need to do multiple posts you can ask more at one post. 

0 Likes
Message 3 of 9

LeoManu
Contributor
Contributor

Sorry, I didn't mean to mix different methods. In that post, the solution would be without modifying the model, which seemed more complicated than just moving the layers in the model. And the last one is about a different topic, which is reorganizing layers by name.

0 Likes
Message 4 of 9

Kent1Cooper
Consultant
Consultant

@LeoManu wrote:

.... I can't seem to sort them by name as they appear in the layer properties. .....


Do you mean alphabetically?  That's only the default order.  You can have them appear in different orders by picking on different column headings to sort by, so "as they appear" is not a constant.

Kent Cooper, AIA
0 Likes
Message 5 of 9

Moshe-A
Mentor
Mentor

@LeoManu hi,

 

here is my version 😀

 

instead of using (getreal), i use (getdist) which also allows you to specify the distance on screen.

plus, instead of selecting all objects for each layer which could select other objects you do not want, i am giving the user to select all objects involve and then filter objects by layer. 

to change the position of the content (layout), you need to specify a criteria?

 

enjoy

Moshe

 

 

 

(defun c:MoveLayersByXDist (/ getLayers filterByLayer  ; local functions
			      xDist ss0 ss1 ctr lay)

 (defun getLayers (/ tbl layerList)
  (while (setq tbl (tblnext "layer" (not tbl)))
   (setq layerList (cons (cdr (assoc '2 tbl)) layerList)) 
  )

  (reverse layerList)
 ); getLayers


 (defun filterByLayer (s0 lay / i s0 ent)
  (setq i -1 s1 (ssadd))
  (repeat (sslength s0)
   (if (eq (strcase (cdr (assoc '8 (entget (setq ent (ssname s0 (setq i (1+ i)))))))) (strcase lay))
    (ssadd ent s1)
   )
  ); repeat

  s1 
 ); filterByLayer 

  
 (setvar "cmdecho" 0)
 (command "._undo" "_begin")
  
 (if (and
       (setq xDist (getdist "\nEnter the 'X distance' value: "))
       (setq ss0 (ssget)) ; select all objects involve in this commnd
     )
  (if (and
	(setq ss1 (filterByLayer ss0 "0")) ; select all on layer zero
	(setq ctr 0)
      )
   (foreach lay (vl-sort (getLayers) (function (lambda (e0 e1) (< (atoi e0) (atoi e1)))))
    (if (and
	  (null (member lay '("0" "1")))
	  (setq ss2 (filterByLayer ss0 lay))
        )
     (progn
      (setq ctr (1+ ctr)) 
      (command "._copy" "_si" ss1 "0,0" (polar '(0.0 0.0 0.0) 0.0 (* xDist ctr)))
      (command "._move" "_si" ss2 "0,0" (polar '(0.0 0.0 0.0) 0.0 (* xDist ctr)))
     )
    ); if
   ); foreach
  ); if
 ); if

 (command "._undo" "_end")
 (setvar "cmdecho" 1)
  
 (princ)
); c:MoveLayersByXDist

 

0 Likes
Message 6 of 9

komondormrex
Mentor
Mentor

hello again,

and another one

(defun c:MoveLayersByXDist ()
  (prompt "\nEnter the 'X distance' value: ")
  (setq xDist (getdist))  ; Prompts the user to enter the X distance

  (if (not xDist)
    (progn
      (prompt "\nError: A numeric value is required for 'X distance'.")
      (exit)
    )
  )

  (setq layers (getLayers)  ; Get a list of all layers
        counter 1  ; Counter to multiply the distance
        selectionLayer0 (ssget "X" (list (cons 8 "0")))  ; Select all objects on layer "0"
        distances '())  ; List to store copy distances

  ; Move all layers except "0" and "1"
  (foreach layer (mapcar 'cdr layers)  
    (if (not (member layer '("0" "1")))  ; Ignore layers "0" and "1"
      (progn
        (setq selection (ssget "X" (list (cons 8 layer))))  ; Select all on the current layer
        (if selection
          (progn
            (setq distance_ (* counter xDist))  ; Calculate the move distance
            (command "_.move" selection "" "0,0,0" (strcat (rtos distance_ 2 6) ",0,0"))  ; Move the selection
            (setq distances (cons distance_ distances))  ; Store the copy distance
            (setq counter (1+ counter))  ; Increment the counter
          )
        )
      )
    )
  )

  ; Copy the contents of layer "0" according to the accumulated distances
  (if selectionLayer0
    (foreach distance_ (reverse distances)  ; Iterate over the distances in order
      (command "_.copy" selectionLayer0 "" "0,0,0" (strcat (rtos distance_ 2 6) ",0,0"))
    )
  )

  (princ "\nLayer movement and layer '0' copy process completed.")
  (princ)
)

(defun normalize_layer_name (name)
	(if (vl-every '(lambda (ascii_) (member ascii_ (vl-string->list "0123456789"))) (vl-string->list name))
		(strcat (substr "0000" 1 (- 4 (strlen name))) name)
		(strcat (substr "0000" 1 (- 4 (strlen (substr name 1 (vl-string-search "_" name))))) name)
	)
)

(defun getLayers (/ layerList)
  (vlax-for layer (vla-get-layers (vla-get-activedocument (vlax-get-acad-object)))
    (setq layerList (append layerList (list (cons (normalize_layer_name (vla-get-name layer)) (vla-get-name layer)))))
  )
  (vl-sort layerList '(lambda (layer_1 layer_2) (< (car layer_1) (car layer_2))))
)
0 Likes
Message 7 of 9

Kent1Cooper
Consultant
Consultant
Accepted solution

@Kent1Cooper wrote:

..... Do you mean alphabetically?  That's only the default order.  ....


If so, and if you can stand having "5_REV_00" coming before just-plain "5", then here's my take on it [minimally tested in your sample drawing]:

 

(defun C:WHATEVER (/ xDist LayName LayList border inc ss)
  (while (not xDist) (setq xDist (getdist "\nX-direction spacing distance: ")))
  (while (setq LayName (cdr (assoc 2 (tblnext "layer" (not LayName)))))
    (if (not (wcmatch LayName "0,1,Defpoints,*|*"))
      ; not those to not move nor Defpoints nor Xref-dependent [last 2 teste expendable?]
      (setq LayList (cons LayName LayList))
    ); if
  ); while
  (setq
    border (ssget "_X" '((8 . "0")))
    inc 0
    LayList (vl-sort Laylist '(lambda (x y) (< (atoi x) (atoi y))))
  ); setq
  (foreach LayName LayList
    (if (setq ss (ssget "_X" (list (cons 8 LayName)))); has object(s)
      (command
        "_.move" ss "" (polar '(0 0 0) 0 (* xDist (setq inc (1+ inc)))) ""
        "_.copy" border "" (polar '(0 0 0) 0 (* xDist inc)) ""
      ); command
    ); if
  ); foreach
  (prin1)
)

 

It counts on your having all Layer names as numbers or starting with numbers.  The reason "5_REV_00" comes before "5" is that it's how (vl-sort) handles them comparatively when sorting by (atoi) on the Layer name.  The compensating advantage over sorting alphabetically is that "150" comes at the end, not before all Layers except "1", and "20" before "3".  It could be made to put "5_REV_00" after "5" with more code, but doing it as it does is one reason it's comparatively short.

 

A couple of comments:

You can incorporate the prompt for X spacing right into the (getdist) function, and if you use that instead of (getreal), you can pick the distance on-screen if you want, in addition to being able to type in a value.  It will handle refusing non-numerical input itself, so mine only has to handle the possibility of Enter [nil return], which results in it asking again.

 

It doesn't wait to ignore Layers 0 and 1 in the Moving part, checking every Layer name before proceeding, but omits them from the list of Layers to work on as it makes that list.  And your starting (setq layerList '()) does nothing -- there is no such thing as a list with nothing in it, but such a thing is simply nil [it doesn't exist].  Yes, a text string can be empty and still exist, and also a selection set, but not a list.

 

It doesn't bother making a list of distances that it then has to go through later to Copy the Layer-0 stuff to each location, but rather just copies the Layer-0 stuff right along with Moving the other-Layer stuff each time.

Kent Cooper, AIA
0 Likes
Message 8 of 9

LeoManu
Contributor
Contributor

Thanks everyone for your help, the code worked perfectly and will help me to study it in depth!

0 Likes
Message 9 of 9

komondormrex
Mentor
Mentor

jist to stick it

(defun c:MoveLayersByXDist ()
	(defun normalize_layer_name (name)
		(if (vl-every '(lambda (ascii_) (member ascii_ (vl-string->list "0123456789"))) (vl-string->list name))
			(strcat (substr "0000" 1 (- 4 (strlen name))) name)
			(strcat (substr "0000" 1 (- 4 (strlen (substr name 1 (vl-string-search "_" name))))) name)
		)
	)
	(defun getLayers (/ layerList)
	  (vlax-for layer (vla-get-layers (vla-get-activedocument (vlax-get-acad-object)))
	    (setq layerList (append layerList (list (cons (vla-get-name layer) (normalize_layer_name (vla-get-name layer))))))
	  )
	  (vl-sort layerList '(lambda (layer_1 layer_2) (< (cdr layer_1) (cdr layer_2))))
	)
	(setq xDist (getdist "\nEnter the 'X distance' value: ")
		  layers (getlayers)
		  selectionLayer0 (ssget "_x" '((8 . "0")))
		  index 1
	)
	(foreach layer (mapcar 'car (vl-remove (assoc "0" layers) (vl-remove (assoc "1" layers) layers)))
		(if (setq selection (ssget "_X" (list (cons 8 layer))))
		          (progn
		            (command "_.move" selection "" "_di" (list (* index xDist) 0 0))
					(command "_.copy" selectionLayer0 "_m" "_si" "" (list (* index xDist) 0 0))
					(setq index (1+ index))
		          )
		)
	)
	(princ)
)
0 Likes