create recctagles automatic

create recctagles automatic

omarsvn
Enthusiast Enthusiast
1,337 Views
16 Replies
Message 1 of 17

create recctagles automatic

omarsvn
Enthusiast
Enthusiast

I created a basic code to let me create a rectangles aligned to a line that form a polyline, I unput the height of the rectangle and the offset that i want respect with the reference line, I choose the first point and the second point and the rectanglehas been created. I would like to improve the code not choosing two points, instead of that choosing the line and the rectangle create. this is my code, is really basic since I'm still learning 

(defun backs ()
(setq d (getdist "\ingrese alto de backsplash"))
(setq o (getdist "\ingrese offset"))
(while
(setvar "osmode" 16383)
(setvar "blipmode" 0)
(setq p1 (getpoint "\seleccione el primer punto")
p2 (getpoint "\seleccione el segundo punto")
)
(setvar "osmode" 0)
(setvar "blipmode" 0)
(command "layer" "s" "fusion" "")
(setq ang (angle p1 p2)
d1 (polar p1 (+ ang (angtof "90")) o)
d2 (polar p2 (+ ang (angtof "90")) o)
d3 (polar d2 (+ ang (angtof "90")) d)
d4 (polar d1 (+ ang (angtof "90")) d))
(command "pline" d4 d1 d2 d3 "c")
))
(defun c:BACK ()
(BACKS))Screenshot (119).png

0 Likes
Accepted solutions (1)
1,338 Views
16 Replies
Replies (16)
Message 2 of 17

tramber
Advisor
Advisor

Great job 😎

(setq obj(car(entsel"\nSelect Line ")))
(setq p1(cdr(assoc 10(entget obj)))
      p2(cdr(assoc 11(entget obj))))

 


EESignature

0 Likes
Message 3 of 17

-didier-
Advisor
Advisor

Bonjour @omarsvn 

 

Assuming the starting entities are lines, you can do this with the joined LSP.
I just wrote it for you, hoping that it will do the job.
Be careful! I just noticed that it only works if the lines are drawn in a clockwise direction,
tell me if I have to correct by putting a control.

 

Amicalement

 

2024-10-11_10-48-40.gif

Éternel débutant.. my site for learning : Programmer dans AutoCAD

DA

EESignature

0 Likes
Message 4 of 17

komondormrex
Mentor
Mentor

check this one

(defun backs ()
	(setq d (getdist "\ingrese alto de backsplash"))
	(setq o (getdist "\ingrese offset"))
	(while
;		(setvar "osmode" 16383)
;		(setvar "blipmode" 0)
;		(setq p1 (getpoint "\seleccione el primer punto")
;		      p2 (getpoint "\seleccione el segundo punto")
;		)
		(setq picked_data (entsel "\nPick pline's linear segment: ")
			  picked_pline (car picked_data)
			  picked_point (vlax-curve-getclosestpointto picked_pline (cadr picked_data))
			  picked_param (fix (vlax-curve-getparamatpoint picked_pline picked_point)) 
			  p1 (vlax-curve-getpointatparam picked_pline picked_param)
			  p2 (vlax-curve-getpointatparam picked_pline (1+ picked_param))
		)
;		(setvar "osmode" 0)
;		(setvar "blipmode" 0)
		(command "layer" "s" "fusion" "")
		(setq ang (angle p1 p2)
		d1 (polar p1 (+ ang (angtof "90")) o)
		d2 (polar p2 (+ ang (angtof "90")) o)
		d3 (polar d2 (+ ang (angtof "90")) d)
		d4 (polar d1 (+ ang (angtof "90")) d))
		(command "pline" "_non" d4 "_non" d1 "_non" d2 "_non" d3 "c")
	)
  )
(defun c:BACK ()
(BACKS))
0 Likes
Message 5 of 17

Moshe-A
Mentor
Mentor

@omarsvn hi,

 

Here is my best version, do not scare 😀 i will explain every thing you need to know and if you have more questions i am here.

 

first the format:

an autocad BACK command \ (c:back) function which wraps every thing inside, this way it's not interfere with other code in autolisp memory.

 

line #1: declare local functions

line #2: declare local variables

 

for now lets skip local functions and jump to line #44 where all starts

line #46 starts UNDO Group

line #83 end    UNDO Group

this wraps all AutoCAD commands run in between for 1 UNDO command.

line #45 and line #84  complements each other, they disable AutoCAD commands echo to the command line window.

 

line #49 is to pick a pline

line #52 make sure a pline is selected

line #53 you specify the offset

line #54 you specify the rectangle width

the above code lines all wrapped in an (if) plus (and) function so if something goes wrong, the function will exist quietly.

 

line #57 gets the Centroid (the middle) point of the pline, for now lets skip the explanation how it done.

line #59 gets the points (geometric) of the pline, again lets skip it for now it is too sophisticated for you.

 

line #64 through #77 is the real job you are interested in

a loop (foreach) through the pline points each edge represented by p0 & p1 points

for each edge a perpendicular is dropped from centroid to the edge, this is to know the angle where to lay the rectangle (outside the pline and not inside)

 

enjoy

Moshe

 

 

 

 

 

 

 

 

(defun c:back (/ _geometric getCentroid sharp-angle near-edge ; local functions
	         pick ename elist of wth c0 points^ p0 p1 bx rm r0 r1 r2 r3)

 ; anonymous function, return pline geometric
 (setq _geometric (lambda (l) (mapcar (function (lambda (item) (cdr item)))
				      (vl-remove-if-not (function (lambda (item) (= (car item) 10))) l))))

 ; return pline centroid 2d point
 (defun getCentroid (ent / AcDbRegion centroid)
  (command "._copy" "_si" ent "_none" "0,0,0" "_none" "0,0,0")
  (command "._region" "_si" (entlast))
  (setq AcDbRegion (vlax-ename->vla-object (entlast))) ; allocating memory
  (setq centroid (vlax-safearray->list (vlax-variant-value (vla-get-centroid AcDbRegion))))
  (vlax-release-object AcDbRegion)    ; dispose memory
  (command "._erase" "_si" (entlast)) ; delete region

  centroid
 ); getCentroid


 ; return triangle sharp angle
 (defun sharp-angle (a0 a1 / a2)
  (if (> a1 a0)
   (setq a2 (- a1 a0))
   (setq a2 (- a0 a1))
  )

  (if (> a2 (* pi 1.5))
   (- (* pi 2) a2)
   a2
  )
 ); sharp-angle 


 ; return triangle near edge length
 (defun near-edge (t0 t1 c0 / a0 a1 cx)
  (setq a0 (angle t0 t1))
  (setq a1 (angle t0 c0))
  (setq cx (distance t0 c0))
  (* (cos (sharp-angle a0 a1)) cx)
 ); near-edge

 
 ; here start c:back
 (setvar "cmdecho" 0)       	; disable command echo
 (command "._undo" "_begin");   ; start command group
  
 (if (and
       (setq pick (entsel "\nPick a pline: ")) 	; pick an object
       (setq ename (car pick))	       		; get entity name
       (setq elist (entget ename))             	; get entiy database
       (eq (cdr (assoc '0 elist)) "LWPOLYLINE")	; is it a pline?
       (setq of (getdist "\nSpecify offset: "))			; get offset
       (setq wth (getdist "\nSpecify rectangle width: "))	; get rectangle width
     )
  (progn
   (setq c0 (getCentroid ename))       ; get pline centroid
   
   (setq points^ (_geometric elist))   ; get pline geometric
   (setq points^ (append points^ (list (car points^)))) ; append first point to list

   (command "._layer" "_make" "fusion" "") ; create "fusion" layer if it is not exist
   
   (setq p0 (car points^))   ; get 1st pline point
   (foreach p1 (cdr points^) ; loop through pline points, chop 1st point
    ; calculater perpendicular point from centroid towards edge
    (setq bx (near-edge p0 p1 c0))
    (setq rm (polar p0 (angle p0 p1) bx)) 
     
    (setq r0 (polar p0 (angle c0 rm) of))         ; 1st rectangle point
    (setq r1 (polar p1 (angle c0 rm) of))	  ; 2nd rectangle point
    (setq r2 (polar p1 (angle c0 rm) (+ of wth))) ; 3th rectangle point
    (setq r3 (polar p0 (angle c0 rm) (+ of wth))) ; 4th rectangle point

    (command "._pline" "_none" r0 "_none" r1 "_none" r2 "_none" r3 "_close")      ; draw rectangle
    (setq p0 p1) ; back safe 1st point
   ); foreach 
    
  ); progn
 ); if


 (command "._undo" "_end") ; end command undo for 1 undo
 (setvar "cmdecho" 1)	   ; enable command echo

 (princ)
); c:back

 

 

0 Likes
Message 6 of 17

Kent1Cooper
Consultant
Consultant

Attached is another way of going about it, which does not require calculating the corners of the rectangles.  It also lets you select as many Polylines as you want to apply the same height and offset to all straight edges of, and it remembers your settings for height and offset and offers them as default values on subsequent use.  It also encloses the whole operation in an Undo begin/end wrapper, and includes *error* handling.

 

It counts on the "fusion" Layer already existing, but that could be built in if needed.  It could also be made to check such things as whether a selected Polyline is made up of only line segments.  [Currently it will accept selection of Polylines containing arc segments, but it will generate the rectangles only from line segments.]

 

It does require Polylines [I assumed from your image in Message 1 that the source was a Polyline], with more than one non-collinear segment, because the area of them is part of the process of ensuring the rectangles are outboard [so it can't work with Lines, which have no area].  But they don't need to be closed.  They do, however need to be convex [again, assuming from your image] -- at a concave corner, the rectangles resulting will be shorter than the edges that meet there.  If you want it to work with Lines, an extra step would be needed to designate to which side you want the rectangle drawn, unless you wouldn't care.

Kent Cooper, AIA
0 Likes
Message 7 of 17

omarsvn
Enthusiast
Enthusiast

Hello, I use this tool for polygons so the code must work with polylines since I manipulate polygon with n numbers of sides and not necessary I need to put rectangles in every edge, only in specific ones. Thank you

0 Likes
Message 8 of 17

omarsvn
Enthusiast
Enthusiast

Your the man!  is there a way that AutoCAD identify a close polygon and draw the rectangles outside of it? I work with close polygons so the rectangles must be drawn outside. When the rectangles is drawn inside I use reverse command on the polygon and I call the back command again and now they are outside but I don't know if it possible to do it 

0 Likes
Message 9 of 17

komondormrex
Mentor
Mentor

you mean to draw a rectangle on every linear segment of a selected closed pline?

0 Likes
Message 10 of 17

omarsvn
Enthusiast
Enthusiast

It works great! The questions is I work with polygon and most of the time I don't need draw rectangles in every edge only in specific ones, so I should choose a side of the polygon and the rectangle must be drawn

0 Likes
Message 11 of 17

omarsvn
Enthusiast
Enthusiast

The code work great, but imagine I have a rectangle and I need to draw rectangle in 2 of its edges, sometimes draw the rectangles inside, and I would like to drawing outside20241011_144157.jpg

0 Likes
Message 12 of 17

omarsvn
Enthusiast
Enthusiast

The code does what I want I just want to draw the rectangles outside everytime20241011_144802_1.gif

0 Likes
Message 13 of 17

Sea-Haven
Mentor
Mentor

With closed plines they can be drawn clockwise or anticlockwise. So there are many solutions to check direction. The default is CCW. You can reverse the direction of the pline simplest way to do it, in Bricscad must use Pedit R as no Reverse command.

 

 

0 Likes
Message 14 of 17

komondormrex
Mentor
Mentor
Accepted solution

check this update with pline's vertices direction checked

(defun is_ccw (segment_list / angle_list)
	(defun diff_angle (angle_1 angle_2)
	  	(setq angle_1 (if (> angle_2 (+ pi angle_1)) (+ (* pi 2) angle_1) angle_1))
	  	(setq angle_2 (if (> angle_1 (+ pi angle_2)) (+ (* pi 2) angle_2) angle_2))
	  	(- angle_2 angle_1)
	)
	(setq angle_list (mapcar '(lambda (segment) (apply 'angle segment)) segment_list))
	(if (> (apply '+ (mapcar '(lambda (angle_1 angle_2) (diff_angle angle_1 angle_2)) angle_list (cdr angle_list))) 0) t nil)
)
(defun backs (/ direction_found was_picked_pline)
	(setq d (getdist "\ingrese alto de backsplash"))
	(setq o (getdist "\ingrese offset"))
	(while (setq picked_data (entsel "\nPick pline's linear segment: "))
	       (if (and
		     	(/= was_picked_pline (setq picked_pline (car picked_data)))
				(null (setq direction_found nil))
		)   
		   (progn
			  (if (not direction_found)
		  		(setq segment_list (mapcar '(lambda (start end) (list (cdr start) (cdr end)))
							    (setq vertices (vl-remove-if '(lambda (group) (/= 10 (car group))) (entget picked_pline)))
							    (append (cdr vertices) (list (car vertices)))
						   )
		  		      ccw_direction (is_ccw segment_list)
			      	      direction_found t
				) 
			  )
		     	  (setq picked_point (vlax-curve-getclosestpointto picked_pline (cadr picked_data))
					picked_param (fix (vlax-curve-getparamatpoint picked_pline picked_point)) 
					p1 (vlax-curve-getpointatparam picked_pline picked_param)
					p2 (vlax-curve-getpointatparam picked_pline (1+ picked_param))
					was_picked_pline picked_pline 
			  )
		     	  (if ccw_direction (setq angle_correction '-) (setq angle_correction '+)) 
		   )
		)
		(command "layer" "s" "fusion" "")
		(setq ang (angle p1 p2)
			d1 (polar p1 ((eval angle_correction) ang (* 0.5 pi)) o)
			d2 (polar p2 ((eval angle_correction) ang (* 0.5 pi)) o)
			d3 (polar d2 ((eval angle_correction) ang (* 0.5 pi)) d)
			d4 (polar d1 ((eval angle_correction) ang (* 0.5 pi)) d)
		)
		(command "_pline" "_non" d4 "_non" d1 "_non" d2 "_non" d3 "_c")
	)
  )
(defun c:BACK ()
(BACKS))
0 Likes
Message 15 of 17

Kent1Cooper
Consultant
Consultant

@omarsvn wrote:

.... I just want to draw the rectangles outside everytime


The code attached at Message 6 accounts for that.

Kent Cooper, AIA
0 Likes
Message 16 of 17

omarsvn
Enthusiast
Enthusiast

I tried but some reason it didn't create a rectangle in the segment of polyline I chose, It's a good improvement that store de height and offset and not writing down every time20241011_144802_1.gif

0 Likes
Message 17 of 17

omarsvn
Enthusiast
Enthusiast

Your the man! it works flawless, it was what I was looking for, thanks