Align Objects to Polyline

Align Objects to Polyline

Anonymous
Not applicable
6,408 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,409 Views
39 Replies
Replies (39)
Message 2 of 40

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

....

I tried the solutions posted ..., 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.

....


For that, it looks like you simply don't have the (vl...) functions loaded.  Add this to the file, either at the very beginning or the very end:

 

(vl-load-com)

 

and load and try it again.

Kent Cooper, AIA
0 Likes
Message 3 of 40

marko_ribar
Advisor
Advisor

("; error: no function definition: VLAX-ENAME->VLA-OBJECT")

                                                                                                             

 

Try to add as the first line inside the code this line :

(vl-load-com)

 

Though I believe this is not the problem - A2012 I suppose have this automatically loaded as far as I know...

Perhaps you should try reinstalling AutoCAD or use repair AutoCAD... This issue came in past - search :

"; error: no function definition: VLAX-ENAME->VLA-OBJECT" and see if there is a topic with better solution...

 

As far as your original question, nesting routines are difficult to program and your case is just one situation that can occur... Note that you firstly used simple array rectangular and trimmed outside sufficient pieces... Your second picture is more economic, but is heavy to automate... So my suggestion : be patient and do it manually as much better as you can - it will still be faster then programming for such automation and beside this - you have freedom to do with pieces whatever transformation you like (rotate, mirror, move, align, scale, ... )

 

HTH., M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes
Message 4 of 40

Kent1Cooper
Consultant
Consultant

I agree that it's a daunting prospect to imagine how that could be automated.  For example, along the longer top-edge segment of your sample boundary, the shapes need to be pushed up so that their top right  corners touch the boundary, but along the shorter upper-left-corner segment, because of the different direction of slope, it would need to be their top left  corners.  A routine would need to account for the direction of the slope of the boundary at a give location, to figure which part of an object to base its position on.

 

And could the boundary ever involve arc  segments?  That would open up the possibility that the same boundary segment could want some objects positioned along it based on a different location  on them than other objects.

 

A routine would need to somehow decide where on such a boundary to start, and in which direction to proceed.  [Fortunately, it is at least possible to determine from a closed Polyline where the inside is vs. the outside -- lots of routines about that on these Forums.]  In the image below, the left one starts with a shape meeting the upper right corner, and proceeds leftward from there along the top edge; the right one starts with one in the lower right [though since it doesn't actually meet  that corner, finding the exact positioning of it by code, to touch both adjacent boundary edges, would be a real challenge], and proceeds leftward along the bottom edge.  The right one gets in one more object, but it may not be possible to find the optimal solution except by running through whatever the process is under all possible scenarios and comparing the results.

FitIn.JPG

 

And what are  the "objects" involved?  If they're single Polylines, it's conceivable that (intersectwith) methods could be used to help position them, and possibly to decide that there isn't room for any in your little tail area on the left, though probably only by putting one in, and finding it intersects the boundary too many times.  But if they're Blocks, or if they're made up of four Lines each, or more than one Polyline, or maybe some other possibilities, it's hard to imagine how they could be positioned.

 

All sorts of questions arise....  Are the "objects" always rectangular, or could they be other shapes?  Are they always orthogonally oriented, or might they be at other angles?  Would a routine ideally somehow determine at what angle  the most can be fit in?  [That would probably have some relationship to the longest boundary segment.]  Is there some standard for the spacing between them, and would that always be the same?  Is there some basis for the difference  between the horizontal and vertical spacings between them?

 

If the objects could be different shapes, something like the left image below might be achievable in the same way as with rectangles, but far less efficient than the right side, which would be extremely difficult to achieve by code, if even possible.

FitIn2.JPG

 

Etc., etc., etc. -- those are just some of the difficulties that come to mind in a quick consideration.

Kent Cooper, AIA
0 Likes
Message 5 of 40

Sea-Haven
Mentor
Mentor

For me it would be pick pt1 pt2 and this gives orientation of alignment axis and a pick pt3 for bottom extent, then use trim to cut out objects outside the boundary shape. Keep repeating. I did something similar but it was a fixed pattern.

0 Likes
Message 6 of 40

hak_vz
Advisor
Advisor

This is one of "maximum packaging" problem that is generally solvable in lisp for simple shape inside irregular polygon. For simple symmetric object like rectangle that only has two (orthogonal) axes of symmetry, solution would involve:

a) Create two sets of lines that are orthogonal, with lines in each set adequately spaced (width + delta and length + delta) and placed inside polygon (trim all outside polygon)

b) Exclude all lines with length lower then threshold value

c) Step around bounding polygon and cut lines so that rectangle placed at start point of each line with rectangle placed in that point (center of edge) don't intersect with bounding polygon edge(s).

d) Divide lines to with threshold value and cut off end segments with length lower than threshold value (*).

e) Calculate total length of all lines, and store parameters for set with bigger total length.

f) Create new set rotated for some delta angle and repeat calculation. If total length in new set is bigger override last one

Max number of rectangles inside polygon should be placed over line set with biggest total length value.

 

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 7 of 40

Kent1Cooper
Consultant
Consultant

I'll add one more to the many question you haven't answered yet:

Do all the shapes, whatever shape they may be, need to be oriented in the same direction as each other, as in your sample image?  For example, is there a "grain" direction in a material that matters?  If so, and subject to answers to other questions, it's almost imaginable that something could be worked out.  But if not, and you could add more shapes at different orientations to fill in the empty areas and get the most out of the material, that seems like something you would need to do manually.

Kent Cooper, AIA
0 Likes
Message 8 of 40

Anonymous
Not applicable

Hi all - thank you all for your valuable input, I appreciate the additional insight.

 

I had written a detailed reply about a week ago, but it seems it got caught in the automatic spam filter or was deleted for some reason (I wrote about it to @Discussion_Admin but have not got a reply), so I'll try to repeat what I said:

 

This is indeed a packing problem I'm trying to solve - albeit a simplified one: The objects will always be rectangles of the same size and orientation, and the polygons will always be closed polylines composed of straight lines.

 

After a bit of research I realized that finding an optimal distribution might be quite difficult, which is why I decided to phrase the problem as aligning objects to a polygon (as even if the resulting distribution is not optimal, it would be better than the unaligned one).

 

For greater simplicity, just reducing the problem to aligning objects to a line might help me make my work much easier. The way I can see this happening algorithmically is as follows:

 

Align objects to edge:

1) Select objects to alignSelected_Obj.jpg

2) Select edge to align to:

Selected_Edge.jpg

3) Select direction of alignment (The objects will only be displaced orthogonally)

4) Check if displacing the objects in this direction causes them to intersect the selected edge. If any of the selected objects does not intersect the edge, throw an error and stop the process. Thus, the following (displacing the objects up) would cause an error, as the three leftmost objects dont intersect the edge:

Error.jpg

This would cause no error however:

OK.jpg

5) If the objects intersect, displace them in the direction given until they intersect the edge, producing the following result:

result.jpg

Unfortunately I'm not sure how to express this in AutoLISP, so any suggestions would be appreciated.

0 Likes
Message 9 of 40

hak_vz
Advisor
Advisor

Create somewhere aside a column of rectangles that spans accros height of bounding polygon. Script works in afollowing way:

1)It asks for a horizontal distance between columns (rectangle width + gap)

2) It asks to select starting end end point of polygon segment on segments on upper side.

3) Looks for a for starting point on a segment

4) Select all rectangles in a temp column or how needed

5) Colects corner point on a topmost rectangle in a column to align with polygon segment

It then copy column accros whole length of a segment

When you jump on a second segment you should select a point on gape distance from last column in a previous segment. I will do it later to calculate automaticaly if you chose my code for further updating.

 

(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)
    (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 v+ (v1 v2)(mapcar '+ v1 v2))
    (defun v- (v1 v2)(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"))	
(setvar 'osmode 512)	
(setvar 'cmdecho 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 accross (abs(/ hor (cos ang))))
	(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 accross))
	(while (< i dis)
		(command "_copy" ss "" pa sp)
		(setq sp (mapcar '+ sp delvect))
		(setq i (+ i accross))
	)

(setvar 'osmode old)
(setvar 'cmdecho 1)
(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 10 of 40

Anonymous
Not applicable

Sorry, I'm not sure I follow what your code is doing. I saved it as "Aligner.lsp", loaded it on the program and tried the command. I select a "horizontal distance" (10), which is the horizontal distance of the objects+offset. Then I select three points on the bounding polygon (First point on bounding segment, second point on bounding segment, starting point on bounding polygon). I then select "first/second point of selection window" around the objects I want to align, but the command only exits with the error "too many arguments". Can you please elaborate on what it should be doing?

0 Likes
Message 11 of 40

hak_vz
Advisor
Advisor

I have writen this during a break by combining my archived rutinse. I'll test it and update in about an houre or so, I'm just about to drive home.

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 12 of 40

ronjonp
Mentor
Mentor

Since this is a packing problem would your end result after manually placing the last pieces maybe look something like this?

image.png

0 Likes
Message 13 of 40

Anonymous
Not applicable

No - this is a "limited" packing problem. All objects must be aligned in the direction given, so there can be no objects with the alignments shown.

0 Likes
Message 14 of 40

hak_vz
Advisor
Advisor

Here is updated code.

 

(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 v+ (v1 v2)(mapcar '+ v1 v2))
    (defun v- (v1 v2)(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))))
	)
    (setvar 'osmode 512)	
    (setvar 'cmdecho 0)
	(setq ang (acos (/ (vect_dot v1 v2) (* ( vect_mod v1)(vect_mod v2)))))
	(setq across (abs(/ hor (cos ang))))
	(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)
)

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 15 of 40

Anonymous
Not applicable

Thank you @hak_vz. I copied it to a new .lsp file and tried again, but I'm still getting the same "too many arguments" error after selecting the second point of the selection window. Could this be related to the version of the program I'm using? (Autocad 2012)

0 Likes
Message 16 of 40

hak_vz
Advisor
Advisor

Ok, I'll create it for version 2012. There was a problem. Update comes in a few minutes,

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 17 of 40

hak_vz
Advisor
Advisor

Try now!

(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 v+ (v1 v2)(mapcar '+ v1 v2))
    (defun v- (v1 v2)(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))))
	)
    (setvar 'osmode 512)	
    (setvar 'cmdecho 0)
	(setq ang (acos (/ (vect_dot v1 v2) (* ( vect_mod v1)(vect_mod v2)))))
	(setq across (abs(/ hor (cos ang))))
	(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)
)

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 18 of 40

hak_vz
Advisor
Advisor

Now it' will have to work correctly. Now it has to be put into loop. It needs logic to detect proper corner to  align to polygon segment (to avoid picking points) and to calculate starting point when it jumps to next segment.

Try to play wit it (if you like it ) and write down what has to be done.

Follow up tomorrow.

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 19 of 40

Kent1Cooper
Consultant
Consultant

Would there ever be situations such as the left here, where the User would need to specify in which direction to Move them [i.e. upward so the bottom left corners touch the Line, or downward so the top right corners do]?  Or the right, in which some should Move up and some down?  Maybe the preceding question should be:  Would the rectangles in their starting positions ever cross the Line [or boundary edge] at all as in these, or would it always be clear exactly where they should go?

MoveToLine.JPG

Kent Cooper, AIA
0 Likes
Message 20 of 40

Anonymous
Not applicable

No, there would be no such situations. The rectangles are generated by the user and can be placed anywhere that is convenient, so those situations can be avoided.

0 Likes