Align Objects to Polyline

Align Objects to Polyline

Anonymous
Not applicable
6,413 Views
39 Replies
Message 1 of 40

Align Objects to Polyline

Anonymous
Not applicable

I need to distribute as many objects as possible within a given polygon.

 

I start with an empty polygon (closed polyline) of arbitrary shape. This gets filled with an array of objects, like so:

 

Orig.jpg

This distribution produces gaps around the edges which results in a suboptimal use of space. Here only 465 objects fit inside the polygon.

 

By pushing all objects against the "walls", I'm able to better use space and get more objects inside the polygon. For instance, by pushing all objects to the "north" wall, I'm able to fit 476 objects instead of the original 465 (3.7% increase):

 

Aligned.jpg

The way I've done this so far is by manually selecting the objects, and displacing them orthogonally until they intersect the polyline I want to align them to.

 

This is very time consuming. Is there some way to do this programatically with LISP or any other method?

 

I'm limited by the fact that I only have Autocad 2012.

 

I tried the solutions posted here (https://forums.autodesk.com/t5/autocad-forum/alignment/m-p/4907312) thinking they might be what I needed, but I get an error ("; error: no function definition: VLAX-ENAME->VLA-OBJECT") which I think is related to the fact that the version of Autocad I have is not the most up to date.

 

Any help would be appreciated.

0 Likes
Accepted solutions (3)
6,414 Views
39 Replies
Replies (39)
Message 21 of 40

Anonymous
Not applicable

Thank you @hak_vz. It still doesnt seem to be working (I keep getting the same "too many arguments" error as before), but you've given me a valuable reference to start looking at how to solve this. I'll look into the code and try to figure out how to make it work.

0 Likes
Message 22 of 40

hak_vz
Advisor
Advisor
Accepted solution

Tested in 2007 and my friend has tested it in 2014 and it works without error.

(defun c:cpyala ( / *error* vector v*s vect_dot v+ v- vect_mod  create_value_vector unit_vect asin acos old hor p1 p2 v1 side v2 dis ang across sp dis a b pa uv i ss) ;
    (defun *error* ()
	(setvar 'osmode old)
	(setvar 'cmdecho 1)
	(princ)
	)
    (defun vector (p1 p2) (mapcar '- p2 p1))
	(defun v*s (v s)(mapcar '* v (create_value_vector (length v) s)))
    (defun vect_dot (v1 v2)(apply '+ (mapcar '* v1 v2)))
    (defun vect_mod (v)(sqrt(vect_dot v v)))
    (defun create_value_vector (n val / r ) (repeat n (setq r (cons val r))) r)
    (defun unit_vect (v)(mapcar '* v (create_value_vector (length v) (/ 1 (vect_mod v)))))
    (defun asin (x)
      (cond 
        ((and(> x -1.0)(< x 1.0)) (atan (/ x (sqrt (- 1.0 (* x x))))))
        ((= x -1.0) (* -1.0 (/ pi 2)))
        ((= x  1) (/ pi 2))
      )
    )
    (defun acos (x)(cond ((and(>= x -1.0)(<= x 1.0)) (-(* pi 0.5) (asin x))))) 
    (defun vectorSide (v1 v2 p / r *fuzz*)
        (setq r (- (* (-(car v2)(car v1))(-(cadr p)(cadr v1)))
                   (* (-(cadr v2)(cadr v1))(-(car p)(car v1)))
                )
            *fuzz* 1e-10
        )
        (cond ((equal r 0.0 *fuzz*) 0) (t (fix (/ (abs r) r))))
    )
(setq old (getvar 'osmode))	

(setq hor (getreal "\nHorizontal distance (rectangle width + gap >"))

	(setq
			p1(getpoint "\nSelect first point on bounding polygon segment >")
			p2(getpoint "\nSelect second point on bounding polygon segment >")
			v1 (vector p1 p2)
			side (vectorSide p1 (list (car p1) (+ (cadr p1) 100)) p2)
			dis (distance p1 p2)
	)
	(if (= side 1)
		(setq v2 (vector p1 (list (- (car p1) 100) (cadr p1))))
		(setq v2 (vector p1 (list (+ (car p1) 100) (cadr p1))))
	)
    
	(setq ang (acos (/ (vect_dot v1 v2) (* ( vect_mod v1)(vect_mod v2)))))
	(setq across (abs(/ hor (cos ang))))
 (setvar 'osmode 2561)	
    (setvar 'cmdecho 0)
    
	(setq sp (getpoint "\n Select starting point on bounding polygon >"))
	(setq dis (- dis (distance sp p1)) i 0)
	(princ "\nSelect objects to align across polygon segment >")
	(setq a (getpoint "\nSelect first point of selection window >"))
	(setq b (getcorner a "\nSelect second point of selection window >"))
	(setq ss (ssget "W" a b))
	(setvar "osmode" 1)	
	(setq pa (getpoint "\nSelect a rectangle corner point to align across segment >"))
	(setq uv (unit_vect v1) delvect (v*s uv across))
	(while (< i dis)
		(command "_.copy" ss "" "none" pa "none" sp)
		(setq sp (mapcar '+ sp delvect))
		(setq i (+ i across))
	)

(setvar 'osmode old)
(setvar 'cmdecho 1)
(princ)
)
(princ "\nType CPYALA to run command!"
(princ)

Procedure:

1) Horizontal distance (rectangle width + gap) > enter value

2) Select first point on bounding polygon segment > Click on oneend of a polygon segment

3 ) Click on end point of polygon segment

Move from polygon left to right or opposite over segments that are at placed up and horizontaly

4)Select starting point on bounding polygon

Osmode changes so that you can pick either starting point or nearest point or intersection somewhere inside range from p1 to p2

5)Select objects to align across polygon segment> You have vertical column of rectangles placed somewhere aside, select them by picking left or right

6) Select a rectangle corner point to align across segment > Pick corner point of rectangle that has to be set on segment between start point (point you selected inside segment range at distance 0 or what applicable to retain horizontal sequence. This is a point on rectangle in temporary column of  topmost rectangle or one on the bottom.

 

Then loop starts and copies from a starting point and in sequence copies temp column to a start point that in every iteration moves from start to end for a vector distance that is a value of horizontal  spacing divided with cosine of a angle between horizontal and vector from start point to end point of polygon segment.

 

Code use functions that are all tested in many occasions, only place where error can emerge is in a command sequence. But it works in a version that is older and newer to 2012. Code needs some polishing but it can be used even it this form.

 

slope06.jpg

 

slope08.jpg

 

slope12.jpg

 

slope14.jpg

 

slope15.jpg

 

slope16.jpg

 

slope17.jpg

 

If this finally start to work on your side, and when we polish code to do some stuff automatically final function will bi written that collects all rectangles outside bounding polygon and those that he intersects with. There is a option to update this script to find most appropriate angle to place max number of rectangles, as I stated in my first post .

 

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.
0 Likes
Message 23 of 40

Anonymous
Not applicable

Thank you @hak_vz ! That looks like it does exactly what I need it to do. Unfortunately I'm still unable to open it in my copy of Autocad - Not sure what I'm doing wrong.

 

I copied the text to notepad, saved the file as "CPYALA.lsp" and dragged/dropped it to Autocad - But this time it fails to load and just says "error: list with incorrect entry format".

 

Maybe it has to do with the program being in Spanish? I dont know what else could be an issue.

0 Likes
Message 24 of 40

hak_vz
Advisor
Advisor

1) Save a file in some directory that is marked as Trusted location, or create new directory to store lisp routines and put it on a list of trusted location > Tools > Options .....

2) Load lisp file Tools > load application  - select file or you can put it in a Startap Suite to make it available whenever you start ACAD

3) Now start command cpyala and it should work ok.

 

UPS.... 

 

(princ "\nType CPYALA to run command!")

Please add ) at the end.

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.
0 Likes
Message 25 of 40

Anonymous
Not applicable

I think the issue I was having was because of a missing parenthesis after "(princ "\nType CPYALA to run command!")". After adding the parenthesis it loads OK.

 

I'm still not able to use it propperly - I think there's a step I'm not getting.

 

1) Type CPYALA

2) Input Horizontal distance: 10

3) Select first point on bounding polygon segment, Select Second Point, Select start point

4) Select object to copy (First and second window point)

 

Then it fails with "too many arguments".

EDIT: Images were in wrong order

 

CPYALA_First_Point.jpgCPYALA_Second_Point.jpgCPYALA_Start_Point.jpgCPYALA_First_Select_Window.jpgCPYALA_Second_Select_Window.jpgCPYALA_too_many_arg.jpg

0 Likes
Message 26 of 40

hak_vz
Advisor
Advisor

When you select objects to copy dont click on them but select them by clicking somewhere aside and let selecting window enclose them. This doesent work as standard select command.

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.
0 Likes
Message 27 of 40

Anonymous
Not applicable

I've also tried this, but its the same. It doesnt select the object to be copied, just says "too many arguments" after I click the second point of the selection window.

0 Likes
Message 28 of 40

hak_vz
Advisor
Advisor

In code replace

(setq a (getpoint "\nSelect first point of selection window >"))
	(setq b (getcorner a "\nSelect second point of selection window >"))
	(setq ss (ssget "W" a b))

with

(setq ss (ssget))

First two line (setq a....) and (setq b...) should be deleted.

 

 

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.
0 Likes
Message 29 of 40

Anonymous
Not applicable

Ok, doing that I'm able to go to the next point - it selects the object, and asks for a "rectangle corner point to align across segment" - Now after I click the corner of the rectangle I want, it fails with "too many arguments".

0 Likes
Message 30 of 40

hak_vz
Advisor
Advisor

Rush houre. Will continue after 16 h CET.

 

(command "_.copy" ss "" "none" pa "none" sp)

Try

(command "_.copy" ss " " pa sp)

 

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.
0 Likes
Message 31 of 40

Anonymous
Not applicable

I tried (command "_.copy" ss " " pa sp) but it still gave an error - It seems the issue was the blank space between the quotes. After deleting it, it seems to work, but I'm still not getting the expected result.

 

Giving a horizontal distance of 10, first point as the leftmost point of the polyline, second point as the first inflection point, start point as the leftmost point, selecting two rectangles and the corresponding cornert, this is the result. This has happened for all lines I've tried with. For reference, the 10 unit spacing should be as in the array above.

 

CPYALA_Result.jpg

0 Likes
Message 32 of 40

hak_vz
Advisor
Advisor
Accepted solution

Try this. Effect happens because of osnaps fire up. Now it should work ok. I hope 🙂

 

(defun c:cpyala () (_cpyala))
(defun _cpyala ( / *error* vector v*s vect_dot v+ v- vect_mod  create_value_vector unit_vect asin acos old hor p1 p2 v1 side v2 dis ang across sp dis a b pa uv i ss ortho) ;
    (defun *error* ()
	(setvar 'osmode old)
	(setvar 'cmdecho 1)
	(princ)
	)
    
    (defun vector (p1 p2) (mapcar '- p2 p1))
	(defun v*s (v s)(mapcar '* v (create_value_vector (length v) s)))
    (defun vect_dot (v1 v2)(apply '+ (mapcar '* v1 v2)))
    (defun vect_mod (v)(sqrt(vect_dot v v)))
    (defun create_value_vector (n val / r ) (repeat n (setq r (cons val r))) r)
    (defun unit_vect (v)(mapcar '* v (create_value_vector (length v) (/ 1 (vect_mod v)))))
    (defun asin (x)
      (cond 
        ((and(> x -1.0)(< x 1.0)) (atan (/ x (sqrt (- 1.0 (* x x))))))
        ((= x -1.0) (* -1.0 (/ pi 2)))
        ((= x  1) (/ pi 2))
      )
    )
    (defun acos (x)(cond ((and(>= x -1.0)(<= x 1.0)) (-(* pi 0.5) (asin x))))) 
    (defun vectorSide (v1 v2 p / r *fuzz*)
        (setq r (- (* (-(car v2)(car v1))(-(cadr p)(cadr v1)))
                   (* (-(cadr v2)(cadr v1))(-(car p)(car v1)))
                )
            *fuzz* 1e-10
        )
        (cond ((equal r 0.0 *fuzz*) 0) (t (fix (/ (abs r) r))))
    )
(setq old (getvar 'osmode))	
(setq ortho (getvar 'orthomode))	
 (setvar 'orthomode 0)
(setq hor (getreal "\nHorizontal distance (rectangle width + gap >"))

	(setq
			p1(getpoint "\nSelect first point on bounding polygon segment >")
			p2(getpoint "\nSelect second point on bounding polygon segment >")
			v1 (vector p1 p2)
			side (vectorSide p1 (list (car p1) (+ (cadr p1) 100)) p2)
			dis (distance p1 p2)
	)
	(if (= side 1)
		(setq v2 (vector p1 (list (- (car p1) 100) (cadr p1))))
		(setq v2 (vector p1 (list (+ (car p1) 100) (cadr p1))))
	)
    
	(setq ang (acos (/ (vect_dot v1 v2) (* ( vect_mod v1)(vect_mod v2)))))
	(setq across (abs(/ hor (cos ang))))
 (setvar 'osmode 2561)	
 (setvar 'cmdecho 0)

    
	(setq sp (getpoint "\n Select starting point on bounding polygon >"))
	(setq dis (- dis (distance sp p1)) i 0)
	(princ "\nSelect objects to align across polygon segment >")
	(setq ss (ssget))
	(setvar "osmode" 1)	
	(setq pa (getpoint "\nSelect a rectangle corner point to align across segment >"))
	(setq uv (unit_vect v1) delvect (v*s uv across))
    (setvar 'osmode 0)
	(while (< i dis)
		(command "_.copy" ss "" "_non" pa "_non" sp)
		(setq sp (mapcar '+ sp delvect))
		(setq i (+ i across))
	)

(setvar 'osmode old)
(setvar 'cmdecho 1)
(setvar 'orthomode ortho)
(princ)
)
(princ)

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.
0 Likes
Message 33 of 40

Anonymous
Not applicable

Perfect! Thank you @hak_vz, it works flawlessly now.

0 Likes
Message 34 of 40

hak_vz
Advisor
Advisor

Finally it works at your side. Test it and try to use it in practice. If you find something that can be added just ask for it.

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.
0 Likes
Message 35 of 40

Anonymous
Not applicable

I have a couple of ideas about how to make it better for my purpose, but I'm not sure how to apply them.

 

First: I usually only have one fixed horizontal distance for a whole project, so having to re-type the horizontal distance for every use is unnecesary. Well - That was easy to fix, just changed:

 

(setq hor (getreal "\nHorizontal distance (rectangle width + gap) >"))

 

to:

 

(setq hor 10)

 

Second: I would like to replace "Select first and second point of bounding polygon segment" to "select line", so that it prompts for an object to be selected and extracts the two endpoints and uses those as P1 and P2.

 

I'm not sure how to do this, but I found this post by @Kent1Cooper and I'm trying to see if I can adapt it to my needs.

0 Likes
Message 36 of 40

hak_vz
Advisor
Advisor

Second: I would like to replace "Select first and second point of bounding polygon segment" to "select line", so that it prompts for an object to be selected and extracts the two endpoints and uses those as P1 and P2.

 

I'm not sure how to do this, but I found this post by @Kent1Cooper and I'm trying to see if I can adapt it to my needs.


I'll fix it later today

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.
0 Likes
Message 37 of 40

hak_vz
Advisor
Advisor
Accepted solution

@hak_vz wrote:

Second: I would like to replace "Select first and second point of bounding polygon segment" to "select line", so that it prompts for an object to be selected and extracts the two endpoints and uses those as P1 and P2


Picking those two points and not extracting from drawing object ( a line) was intentional. If you can ensure that your line object will always be drawn to direction you fill bounding polygon then code would work correctly. If you bounding object would be created as a polyline and then exploded and if it was created with vertexes collected in oposite direction (so that vector p1 p2 looks in oposite direction result would be wrong).
To always have good result draw your lines from left to rigth or oposite, but always in direction you fill bounding polygon, and let those lines always be LINE.

 

(defun c:cpyala () (_cpyala))
(defun _cpyala ( / *error* vector v*s vect_dot v+ v- vect_mod  create_value_vector unit_vect asin acos old hor p1 p2 v1 side v2 dis ang across sp dis a b pa uv i ss ortho) ;
    (defun *error* ()
	(setvar 'osmode old)
	(setvar 'cmdecho 1)
	(princ)
	)
    
    (defun vector (p1 p2) (mapcar '- p2 p1))
	(defun v*s (v s)(mapcar '* v (create_value_vector (length v) s)))
    (defun vect_dot (v1 v2)(apply '+ (mapcar '* v1 v2)))
    (defun vect_mod (v)(sqrt(vect_dot v v)))
    (defun create_value_vector (n val / r ) (repeat n (setq r (cons val r))) r)
    (defun unit_vect (v)(mapcar '* v (create_value_vector (length v) (/ 1 (vect_mod v)))))
    (defun asin (x)
      (cond 
        ((and(> x -1.0)(< x 1.0)) (atan (/ x (sqrt (- 1.0 (* x x))))))
        ((= x -1.0) (* -1.0 (/ pi 2)))
        ((= x  1) (/ pi 2))
      )
    )
    (defun acos (x)(cond ((and(>= x -1.0)(<= x 1.0)) (-(* pi 0.5) (asin x))))) 
    (defun vectorSide (v1 v2 p / r *fuzz*)
        (setq r (- (* (-(car v2)(car v1))(-(cadr p)(cadr v1)))
                   (* (-(cadr v2)(cadr v1))(-(car p)(car v1)))
                )
            *fuzz* 1e-10
        )
        (cond ((equal r 0.0 *fuzz*) 0) (t (fix (/ (abs r) r))))
    )
(setq old (getvar 'osmode))	
(setq ortho (getvar 'orthomode))	
(setvar 'orthomode 0)
(setq hor 10)

	(setq
			e (entsel "\nSelect segment line >")
			ent (entget (car e))
			p1 (cdr(assoc 10 ent))
			p2 (cdr (assoc 11 ent))
			;p1(getpoint "\nSelect first point on bounding polygon segment >")
			;p2(getpoint "\nSelect second point on bounding polygon segment >")
			v1 (vector p1 p2)
			side (vectorSide p1 (list (car p1) (+ (cadr p1) 100)) p2)
			dis (distance p1 p2)
	)
	(if (= side 1)
		(setq v2 (vector p1 (list (- (car p1) 100) (cadr p1))))
		(setq v2 (vector p1 (list (+ (car p1) 100) (cadr p1))))
	)
    
	(setq ang (acos (/ (vect_dot v1 v2) (* ( vect_mod v1)(vect_mod v2)))))
	(setq across (abs(/ hor (cos ang))))
 (setvar 'osmode 2561)	
 (setvar 'cmdecho 0)

    
	(setq sp (getpoint "\n Select starting point on bounding polygon >"))
	(setq dis (- dis (distance sp p1)) i 0)
	(princ "\nSelect objects to align across polygon segment >")
	(setq ss (ssget))
	(setvar "osmode" 1)	
	(setq pa (getpoint "\nSelect a rectangle corner point to align across segment >"))
	(setq uv (unit_vect v1) delvect (v*s uv across))
    (setvar 'osmode 0)
	(while (< i dis)
		(command "_.copy" ss "" "_non" pa "_non" sp)
		(setq sp (mapcar '+ sp delvect))
		(setq i (+ i across))
	)

(setvar 'osmode old)
(setvar 'cmdecho 1)
(setvar 'orthomode ortho)
(princ)
)
(princ)

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.
0 Likes
Message 38 of 40

Anonymous
Not applicable

Perfect, thank you very much. It seems to be working as expected, even though Im using exploded polylines. This definitely speeds up the process a lot.

0 Likes
Message 39 of 40

hak_vz
Advisor
Advisor

Try also this helper function (delcross).

 Keep your bounding polygon as a polyline object, or join line segments into a polyline (magenta polyline on image below). For all elements that are crossing with bounding polygon or are underneath create closed polyline that encloses them and also include lower segments of bounding polygon (blue polyline) . Use provided function delcross to erase all unnecessary blocks. Test it to see if it  really works  ok.

 slope4.jpg

 

 

slope5.jpg

 

slope6.jpg

(defun c:delcross () (_delcross))
(defun _delcross ( / pts e d ent  clos ss i) 
(setq 
    d (car(entsel "\n Select bounding polygon >"))
    e (car(entsel "\n Select cutting edge polyline >"))
    ent (entget e)
    clos (cdr (assoc 70 ent))
    i 0 
)
(repeat (length ent)
    (if (eq (car (nth i ent)) 10) 
        (setq pts (append pts (list (cdr (nth i ent)))))
    )
    (setq i (+ i 1))
)
(if (= clos 1) 
    (setq pts (cons (car pts) pts))
)
(setq 
    ss (ssget "CP" pts)
    i 0
)
(setq ss (ssdel d ss))
(setq ss (ssdel e ss)) ; remove this line to also delete eraser polygon
(if ss
    (while (< i (sslength ss))
        (entdel (ssname ss i))
        (setq i (+ i 1))
    )
)
(princ "\nDone")
(princ) 
)

 

 

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.
0 Likes
Message 40 of 40

hak_vz
Advisor
Advisor
(defun c:delcross ( / pts e d ent  clos ss i) 
    (setq 
        d 
            (car(entsel "\n Select bounding polygon >"))
        e 
            (car(entsel "\n Select cutting edge polyline >"))
        ent 
            (entget e)
        clos 
            (cdr (assoc 70 ent))
         i 
0 ) (repeat (length ent) (if (eq (car (nth i ent)) 10) (setq pts (append pts (list (cdr (nth i ent)))) ) ) (setq i (+ i 1) ) ) (if (= clos 1) (setq pts (append pts (list(car pts))) ) ) (setq ss (ssget "CP" pts) i 0 ss (ssdel d ss) ss (ssdel e ss) ) (if ss (while (< i (sslength ss)) (entdel (ssname ss i)) (setq i (+ i 1)) ) ) (princ "\nDone") (princ) )

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.
0 Likes