copy block into multiple polygons (closed polylines)

copy block into multiple polygons (closed polylines)

sazurich
Participant Participant
1,913 Views
19 Replies
Message 1 of 20

copy block into multiple polygons (closed polylines)

sazurich
Participant
Participant

Hello everyone

I've got a little problem and would greatly appreciate if someone could help.

I am in search of lisp that can do this thing :D, if its possible.

I want to be able to copy (not create new) my already created block (same attributes) and paste it into multiple polygons (or closed polylines, don't know if there is difference).

It can be placed anywhere inside polygon (just not touching boundary).

So i would want to be able to start command, select my block, enter, select all polygons (with green select box), press enter and that would be that.

 

Thx in an advance

 

 

Snimka zaslona 2023-08-21 142245.png

 

0 Likes
Accepted solutions (1)
1,914 Views
19 Replies
Replies (19)
Message 2 of 20

paullimapa
Mentor
Mentor

This should get you started. All you need to do is modify the code from inserting the block to copying the block

https://www.cadtutor.net/forum/topic/71203-lisp-place-block-on-geometric-center-of-polyline/


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 3 of 20

Kent1Cooper
Consultant
Consultant

@sazurich wrote:

.... select all polygons (with green select box), ....


To "see" only those that are closed as in your Topic heading and image, whether or not linetype generation is enabled in any or all of them, use this for the selection part:

(ssget '((0 . "LWPOLYLINE") (-4 . "&") (70 . 1)))

Kent Cooper, AIA
0 Likes
Message 4 of 20

hosneyalaa
Advisor
Advisor

Hi 

Can you attached example drawing 

 

0 Likes
Message 5 of 20

paullimapa
Mentor
Mentor

Give cBlkCen.lsp a try....Note: Block's insertion point needs to be located in center of block in order for it to be positioned in center of closed plines:

; cBlkCen copies block in center of selected closed plines
; https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/copy-block-into-multiple-polygons-closed-polylines/m-p/12186636#M453666
(defun c:cBlkCen 
  (/ _getgeoc blk ed ent i pt pt1 ss) ; localize functions & variables
  ;; function to get geometric center of closed pline
  ;; For Example:
  ;; (_getgeoc en) 
  ;; Where en is a Pline entity
  ;; Returns cooridinate point location
  (defun _getgeoc (en / allx ally ctr el vertex x y)
   (setq el (entget en) ; get entity data
         allx 0
         ally 0
         ctr  0   
   )
   (while (assoc 10 el) ; cycle through vertices
    (setq	vertex (cdr (assoc 10 el)) 
          ctr    (+ ctr 1)           ; keep count of # of vertices 
          x      (car vertex)        ; get x coordinate
          y      (cadr vertex)       ; get y coordinate
          allx   (+ allx x)          ; add up x coordinates
          ally   (+ ally y)          ; add up y coordinates
          EL     (cdr (member (assoc 10 el) el)) ; get next vertex
    )
   )
   (setq x (/ allx ctr) ; divide sum of x with # of x to get average
         y (/ ally ctr) ; divide sum of y with # of y to get average
   )
   ; return coordinate
   (list x y) 
  ) 
  (princ"\nSelect Block to Copy...")
  (while(not blk)
   (setq blk(ssget "_+.:E:S" '((0 . "INSERT"))))
   (if blk
    (progn
     (setq ed(entget(ssname blk 0))) ; get entity data
     (setq pt(cdr(assoc 10 ed))) ; get block insertion point
    )
    (setq blk nil)
   )
  )
  (princ"\nSelect Closed Plines...")
  (if(setq ss (ssget '((0 . "LWPOLYLINE") (-4 . "&") (70 . 1))))
   (progn
    (setq i -1)
    (while (setq ent (ssname ss (setq i (1+ i))))
      (setq pt1 (_getgeoc ent)) ; get center of closed pline
      (vl-cmdf "_.Copy" blk "" pt pt1) ; copy block
    ) ; while
   ) ; progn
   (princ"\nNo Closed Plines Selected.")
  ) ; if
  (princ)
) ; defun

 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 6 of 20

sazurich
Participant
Participant

this doesn't work properly. i checked my insertion point for block and its throwing them outside polygons. not few but all blocks are offset same distance and direction 😄

 

EDIT: i found this code, i am not that good at making lisps (never made one), but as i can gather i just need the last part of this code for copying blocks.

 

EDIT 2: btw i don't know how do you paste code in here to look that nice like yours with gray background and line numbering

 

;; by Gilles Chanteau (_gile)
(defun gc:MostInnerPoint (obj fuzz / 2d-coord->pt-lst 3d-coord->pt-lst dich-sub len tmp)

(defun 2d-coord->pt-lst (lst)
(if lst (cons (list (car lst) (cadr lst)) (2d-coord->pt-lst (cddr lst))))
);end_defun

(defun 3d-coord->pt-lst (lst)
(if lst (cons (list (car lst) (cadr lst) (caddr lst)) (3d-coord->pt-lst (cdddr lst))))
);end_defun

(defun dich-sub (inf sup / of new pts)
(if (equal inf sup fuzz)
(progn
(setq of (vlax-invoke obj 'Offset inf)
pts (if (= (vla-get-ObjectName (car of)) "AcDbPolyline")
(2d-coord->pt-lst (vlax-get (car of) 'Coordinates))
(3d-coord->pt-lst (vlax-get (car of) 'ControlPoints))
);end_if
);end_setq
(mapcar 'vla-delete of)
(mapcar (function (lambda (x) (/ x (length pts)))) (apply 'mapcar (cons '+ pts)))
);end_progn
(progn
(setq new (/ (+ inf sup) 2.0)
of (vl-catch-all-apply 'vlax-invoke (list obj 'Offset new))
);end_setq
(if (vl-catch-all-error-p of)
(dich-sub inf new)
(progn
(mapcar 'vla-delete of)
(dich-sub new sup)
)
);end_if
);end_progn
);end_if
);end_defun

(if (and (member (vla-get-ObjectName obj) '("AcDbPolyline" "AcDbSpline"))
(vlax-curve-isClosed obj)
(or (= (vla-get-ObjectName obj) "AcDbPolyline")
(vlax-curve-isPlanar obj)
);end_or
(setq tmp (vl-catch-all-apply 'vlax-invoke (list obj 'Offset fuzz)))
(setq len (vlax-curve-getDistAtParam obj (vlax-curve-getEndParam obj))
tmp (car tmp)
);end_setq
(if (< len (vlax-curve-getDistAtParam tmp (vlax-curve-getEndParam tmp)))
(setq len (/ len (* -2 pi)))
(setq len (/ len (* 2 pi)))
);end_if
(not (vla-delete tmp))
);end_and
(dich-sub 0.0 len)
);end_if
);end_defun (MostInnerPoint)

(vl-load-com)

(defun c:bmip ( / *error* c_doc c_spc sv_lst sv_vals blk bname ss l_obj i_pt n_obj)

(defun *error* ( msg )
(mapcar 'setvar sv_lst sv_vals)
(if (not (wcmatch (strcase msg) "*BREAK*,*CANCEL*,*EXIT*")) (princ (strcat "\nOops an Error : " msg " occurred")))
(princ)
);end_defun

(setq c_doc (vla-get-activedocument (vlax-get-acad-object))
c_spc (vlax-get-property c_doc (if (= 1 (getvar 'cvport)) 'paperspace 'modelspace))
sv_lst (list 'cmdecho 'osmode)
sv_vals (mapcar 'getvar sv_lst)
);end_setq

(mapcar 'setvar sv_lst '(0 0))

(setq blk (vlax-ename->vla-object (car (entsel "\nSelect Block to Insert : ")))
bname (vlax-get blk 'name)
ss (ssget '((0 . "LWPOLYLINE") (70 . 1)))
);end_setq

(cond (ss
(repeat (setq cnt (sslength ss))
(setq l_obj (vlax-ename->vla-object (ssname ss (setq cnt (1- cnt))))
i_pt (gc:MostInnerPoint l_obj 0.01)
n_obj (vla-InsertBlock c_spc (vlax-3d-point i_pt) bname 1 1 1 0)
);end_setq
(vlax-put-property n_obj 'layer (vlax-get-property blk 'layer))
);end_repeat
)
(t (alert "No Polylines Selected"))
);end_cond

(mapcar 'setvar sv_lst sv_vals)
(princ)
);end_defun

 

my problem is it works on most closed polyline but some that are really narrow and broken sharply, function is still pasting block outside said polygon

Don't know if the problem is in this line : i_pt (gc:MostInnerPoint l_obj 0.01)

maybe number needs to be even lower?

 

 

0 Likes
Message 7 of 20

komondormrex
Mentor
Mentor

hi,

why do you want to multiple copy a block, does it got transformed from its definition?

0 Likes
Message 8 of 20

paullimapa
Mentor
Mentor

Click on this symbol </> to add code which preserves the formatting 

Could you share your drawing with the closed plines and the block you want to place inside then so I can test what’s causing the lisp to fail?


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 9 of 20

ВeekeeCZ
Consultant
Consultant

@sazurich wrote:

...

EDIT: i found this code, i am not that good at making lisps (never made one), but as i can gather i just need the last part of this code for copying blocks.

...


 

IMHO That Gile's code is for 3D polylies and splines only. Not sure whether he has a version for LWPPOLYLINES too. 

@_gile 

0 Likes
Message 10 of 20

komondormrex
Mentor
Mentor

not elegant but roughly effective approach to find averaged center of farthest inside offset for target closed pline with set stepping.

;*************************************************************************************************************************************************

(defun find_point_inside_closed_pline (pline_object / pline_object+1 offset_value offset_stepping offset_direction offset_variant
				       		      index point_inside_sum_x point_inside_sum_y
				      )
	(setq pline_object+1 (car (vlax-invoke pline_object 'offset 0.1))
	      offset_value 0
	      offset_stepping 0.5
	      offset_direction (if (> (vla-get-area pline_object+1) (vla-get-area pline_object)) -1 1)
	)
	(vla-erase pline_object+1)
	(while (null (vl-catch-all-error-p
		     	(setq offset_list
			 	(vl-catch-all-apply 'vlax-invoke
					  	    (list pline_object
							  'offset
							  (* offset_direction
							     (setq offset_value (+ offset_value offset_stepping))
							  )
						    )
				)
	           	)
		     )
		)
		(foreach offset_pline offset_list
			(setq raw_coordinates (vlax-get offset_pline 'coordinates))
			(vla-erase offset_pline)
		)
	)
	(setq index 0
	      point_inside_sum_x 0
	      point_inside_sum_y 0
	)
	(repeat (/ (length raw_coordinates) 2)
		(setq point_inside_sum_x (+ point_inside_sum_x (nth index raw_coordinates))
		      point_inside_sum_y (+ point_inside_sum_y (nth (1+ index) raw_coordinates))
		      index (+ index 2)
		)
	)
    	(trans (list (/ point_inside_sum_x (/ (length raw_coordinates) 2)) (/ point_inside_sum_y (/ (length raw_coordinates) 2))) 0 1)
)

;*************************************************************************************************************************************************

(defun c:copy_block_inside_closed_pline (/ closed_pline_sset block_to_copy)
	(prompt "Select closed plines to copy block inside")
	(setq closed_pline_sset (ssget '((0 . "lwpolyline") (-4 . "&=") (70 . 1))))
	(if closed_pline_sset
	  	(progn
			(setq block_to_copy (vlax-ename->vla-object (car (entsel "\Pick a block to copy inside each closed pline selected: "))))
			(foreach pline (mapcar 'vlax-ename->vla-object (vl-remove-if 'listp (mapcar 'cadr (ssnamex closed_pline_sset))))
				(vla-move (vla-copy block_to_copy)
					  (vla-get-insertionpoint block_to_copy)
					  (vlax-3d-point (find_point_inside_closed_pline pline))
			  	)
			)
	  	)
  	)
  	(princ)
)

;*************************************************************************************************************************************************

 

 

 

 

Message 11 of 20

ВeekeeCZ
Consultant
Consultant

Like it. I would just change those constants to fractions of the area or perimeter. Just to get a notion of scale.

0 Likes
Message 12 of 20

sazurich
Participant
Participant

here i attached drawing that i want lisp to be used on. in my last post lisp almost works perfectly but some blocks just got put outside polygons. i am guessing they are being copied on centroid of polygon, and what i would like it to do is to just put it anywhere inside 😄

 

so if anyone can figure that out would be of great help

 

0 Likes
Message 13 of 20

paullimapa
Mentor
Mentor

wow, you have so many little tiny closed plines everywhere.

I've tested a few and for those the block does fall inside the shape.

The code also is not using the geometric center but the average of all the vertices.

Of course the block is larger so it's not going to fit physically inside the shape:

paullimapa_0-1692803091438.png

Perhaps you can just include in the dwg the shapes where the block gets inserted outside and I can test just those?


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 14 of 20

sazurich
Participant
Participant

first of all thx for testing, cant thank you enough.

 

here is the dwg.

the first right rectangle is filled will all the previous plines (including the ones that are not working), and second named 'not inside' (layer name, color red) are separated into left rectangle. you can see the pattern of the closed plines that are not working properly, they are all bent a little, the straight ones are working like charm. so 99% percent of them work 😄

 

well at least with the lisp that i posted above.

 

Edit: i dont need whole block to fit inside (i can adjust its size how ever i want), i just need his insertion point(center) to be inside (not touching line) 😄

0 Likes
Message 15 of 20

sazurich
Participant
Participant

friendly bump . still looking for solution if its even possible 😄

0 Likes
Message 16 of 20

paullimapa
Mentor
Mentor

Not from the functions I’ve tried. I’ve not been able to get a function that covers all cases. They always miss in the ones you pointed out in your sample dwg 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 17 of 20

Kent1Cooper
Consultant
Consultant

@sazurich wrote:

.... i dont need whole block to fit inside ..., i just need his insertion point(center) to be inside (not touching line) 


So the problem is in the not-just-rectangular ones, like this:

Kent1Cooper_0-1693403665557.png

The white is one of your shapes, the yellow is your Block inserted at the Centroid of a Region made from it [for some reason GeometricCEnter Osnap, which is supposed to give the Centroid of a Polyline, gives something far away], and the green is one inserted at the middle of its bounding box.  I didn't look at averaging the vertex locations, which should be close to the centroid.

 

Unfortunately, there is not the kind of consistency I was hoping to find among the bent shapes.  In this particular one, the starting vertex is at one end [bottom] of one of the ends [left], with the last segment being the short end there.  If they were all like that, there might be some hope of figuring out a midway point between opposite intermediate segment midpoints or something, complicated by the varying number of segments [up to 14 at least].  But they're not consistent -- some of them start somewhere partway along a long side, not at an end.  So for those, the "opposite intermediate segments" it finds could be the short ends, in which case halfway between their midpoints could end up outside for a different reason than above.  And similarly if it looks at midway between what should be opposite vertices, rather than endpoints [depending on how many segments].

 

There may be a way to narrow it down, looking at all the segment lengths, and figuring what might be midway segments between the shortest ones, but can it be assumed that there will never be a bend/jog part with shorter segments than the end ones?  Etc.

Kent Cooper, AIA
0 Likes
Message 18 of 20

ВeekeeCZ
Consultant
Consultant

Try this a bit enhanced code from @komondormrex 

It leaves an active selection with possible issues.

 

;; written by komondormrex;; https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/copy-block-into-multiple-polygons-closed-polylines/m-p/12191133/highlight/true#M453738
;; mods by beekeecz

;*************************************************************************************************************************************************

(defun find_point_inside_closed_pline (pline_object / default_offset pline_object+1 offset_value offset_stepping offset_direction offset_variant
				       index point_inside_sum_x point_inside_sum_y point inters_x inters_y line_y line_x
				       )
  (setq default_offset (/ (vla-get-length pline_object) 1000)
	pline_object+1 (car (vlax-invoke pline_object 'offset default_offset))
	offset_value 0
	offset_stepping default_offset
	offset_direction (if (> (vla-get-area pline_object+1) (vla-get-area pline_object)) -1 1)
	)
  (vla-erase pline_object+1)
  (while (null (vl-catch-all-error-p
		 (setq offset_list
			(vl-catch-all-apply 'vlax-invoke
			  (list pline_object
				'offset
				(* offset_direction
				   (setq offset_value (+ offset_value offset_stepping))
				   )
				)
			  )
		       )
		 )
	       )
    (foreach offset_pline offset_list
      (setq raw_coordinates (vlax-get offset_pline 'coordinates))
      (vla-erase offset_pline)
      )
    )
  (setq index 0
	point_inside_sum_x 0
	point_inside_sum_y 0
	)
  (repeat (/ (length raw_coordinates) 2)
    (setq point_inside_sum_x (+ point_inside_sum_x (nth index raw_coordinates))
	  point_inside_sum_y (+ point_inside_sum_y (nth (1+ index) raw_coordinates))
	  index (+ index 2)
	  )
    )
  (setq point (list (/ point_inside_sum_x (/ (length raw_coordinates) 2)) (/ point_inside_sum_y (/ (length raw_coordinates) 2))))
  
  (setq line_x (entmakex (list (cons 0 "LINE") (cons 10 point) (cons 11 (mapcar '+ '(1 0 0) point)))))
  (setq inters_x (vlax-invoke pline_object 'intersectwith (vlax-ename->vla-object line_x) acextendotherentity))

  (if (> (length inters_x) 5)
    (setq point (list (/ (+ (car inters_x) (cadddr inters_x)) 2)
		      (cadr point)
		      0.))
    (ssadd (vlax-vla-object->ename pline_object) ss_failed))
  
  (setq line_y (entmakex (list (cons 0 "LINE") (cons 10 point) (cons 11 (mapcar '+ '(0 1 0) point)))))
  (setq inters_y (vlax-invoke pline_object 'intersectwith (vlax-ename->vla-object line_y) acextendotherentity))
  
  (if (> (length inters_x) 5)
    (setq point (list (car point)
		      (/ (+ (cadr inters_y) (nth 4 inters_y)) 2)
		      0.))
    (ssadd (vlax-vla-object->ename pline_object) ss_failed))
  
  (entdel line_x) (entdel line_y)
  (trans point 0 1)
  )

;*************************************************************************************************************************************************

(defun c:copy_block_inside_closed_pline (/ closed_pline_sset block_to_copy)

  (setq ss_failed (ssadd))
  
  (prompt "\nSelect closed plines to copy block inside")
  (setq closed_pline_sset (ssget '((0 . "lwpolyline") (-4 . "&=") (70 . 1))))
  (if closed_pline_sset
    (progn
      (setq block_to_copy (vlax-ename->vla-object (car (entsel "\nPick a block to copy inside each closed pline selected: "))))
      (foreach pline (mapcar 'vlax-ename->vla-object (vl-remove-if 'listp (mapcar 'cadr (ssnamex closed_pline_sset))))
	(vla-move (vla-copy block_to_copy)
		  (vla-get-insertionpoint block_to_copy)
		  (vlax-3d-point (find_point_inside_closed_pline pline))
		  )
	)
      )
    )

  (if (> (sslength ss_failed) 0) (sssetfirst nil ss_failed))
  (setq ss_failed nil)
  
  (princ)
  )

;*******************

 

0 Likes
Message 19 of 20

komondormrex
Mentor
Mentor
Accepted solution

@ВeekeeCZ 

this one checks intersections of xline and a target pline perpendicular to pline 1st derivative at one fourth perimeter point of a target pline and if they are two points places a selected block right between them points . may obviously have flaws, but puts 780 blocks out of 789 plines in sample drawing. 9 supposedly have z-coordinates issues. 

;**********************************************************************************************************************************************

;	komondormrex, sep 2023

;**********************************************************************************************************************************************

(defun get_intersection_point (check_xline_object pline / intersection_point intersection_point_list)
	(setq intersection_point (vlax-invoke check_xline_object
					     'intersectwith
					      pline
					      acextendnone
				 )
	)
	(if (= 6 (length intersection_point))
		(progn
		  	(repeat 2
				(setq intersection_point_list (append intersection_point_list
								      (list (list (car intersection_point)
										  (cadr intersection_point)
										  (caddr intersection_point)
									    )
								      )
							      )
				      intersection_point (cdddr intersection_point)
				)
			)
		  	(polar (car intersection_point_list)
			       (angle (car intersection_point_list) (cadr intersection_point_list))
			       (* 0.5 (distance (car intersection_point_list) (cadr intersection_point_list)))
			)
	  	)
		nil
	)
)

;**********************************************************************************************************************************************

(defun c:copy_block_inside_closed_pline (/ closed_pline_sset block_to_copy xline_object fourth_perimeter point_fourth_perimeter 1st_derivative)
	(prompt "Select closed plines to copy block inside")
	(setq closed_pline_sset (ssget '((0 . "lwpolyline") (-4 . "&=") (70 . 1))))
	(if closed_pline_sset
	  	(progn
			(setq block_to_copy (vlax-ename->vla-object (car (entsel "\Pick a block to copy inside each closed pline selected: ")))
		  	      xline_object (vla-addxline (vla-get-block (vla-get-activelayout (vla-get-activedocument (vlax-get-acad-object))))
					  	      	 (vlax-3d-point '(0 0))
					  	      	 (vlax-3d-point '(1 0))
					   )
			      block_placed 0
			      block_missed 0
			)
		  	(terpri)
			(foreach pline (mapcar 'vlax-ename->vla-object (vl-remove-if 'listp (mapcar 'cadr (ssnamex closed_pline_sset))))
				(setq fourth_perimeter (* 0.25 (vla-get-length pline))
				      point_fourth_perimeter (vlax-curve-getpointatdist pline fourth_perimeter)
				      1st_derivative (vlax-curve-getfirstderiv pline (vlax-curve-getparamatpoint pline point_fourth_perimeter))
				)
			  	(vlax-put xline_object 'basepoint point_fourth_perimeter) 
			  	(vlax-put xline_object 'directionvector 1st_derivative)
			  	(vlax-invoke xline_object 'rotate point_fourth_perimeter (* 0.5 pi))
			  	(if (setq block_point (get_intersection_point xline_object pline))
					(progn
					  	(vla-move (vla-copy block_to_copy)
							  (vla-get-insertionpoint block_to_copy)
							  (vlax-3d-point block_point)
					  	)
					  	(setq block_placed (1+ block_placed))
					)
				  	(progn
						(vla-put-color pline 6)
					  	(setq block_missed (1+ block_missed))
				  	)
			  	)
				(princ (strcat "\rPolylines with placed blocks: " (itoa block_placed) ", "
					       "polylines with missed blocks (magenta): " (itoa block_missed)
				       )
			        )
		  	)
	  	)
  	)
  	(vla-erase xline_object) 
  	(princ)
)

;**********************************************************************************************************************************************

 

0 Likes
Message 20 of 20

sazurich
Participant
Participant

thanks alot guys. i wasnt able to be here as of late but solution from @komondormrex is as close as i could hoped for to work. it cuts work so much, its not problem to then just manually put few block in closed plines.

thx again

EDIT: changing color of missed polygons into magenta is really nice touch 😄

0 Likes