Jump to content

block spacing lisp


drew4748

Recommended Posts

Good morning everyone.

 

I have a question regarding an idea for a lisp. I am wondering if this is possible, or something to good to be true.

 

I work at a solar panel racking company, we use CAD to populate modules onto the tops of roofs. These modules are assigned colours in order to determine the ballast weight. Each colour has a specific ballast block spacing, which changes from job to job. The ballast blocks, modules and colouring zones are all on blocks which remain the same through out all of our drawings.

 

Currently we copy and paste each block into our drawings, some drawings have over 3000 blocks. This is very time consuming and I know there is a better way to do this....some how. Is there a way where a routine could prompt the user for the spacing in each zone and populate the layout with ballast blocks; either populating one row at a time or the entire drawing.

 

Thank you in advance.

Link to comment
Share on other sites

This sounds like a job for the ARRAY command.

Is there anything I am missing?

 

(It asks x-spacing, y-spacing, then copy/pastes what ever the client selected)

Link to comment
Share on other sites

Array would not work because each zone has a different spacing. It is as though i need a "smart" array which can determine what zone it is populating; does this make sense. I will attach a picture below to show how the zones change. As you can see, the simple array command will not work; unless there is something I am missing. kXBlMsPQAAAAASUVORK5CYII=kXBlMsPQAAAAASUVORK5CYII=

Link to comment
Share on other sites

Let me get an idea of what exacly you have to do.

 

When you start drawing, those rectangles with colors are already there, and you have to fill in those small, vertical rectangles.

example: row 30: the non-hatched region (between the two green regions) requires a ballast every 1200 length units; while the red region on row 29 requires a ballast every 600 units.

Right?

 

What do those colored rectangles look like? Are those blocks? Is this hatch filled in manually or is there some button (visibility parameter) to change the color of the hatch? Do they contain an attribute with the spacing value ? ... Anything like that?

Link to comment
Share on other sites

Let me get an idea of what exactly you have to do.

 

When you start drawing, those rectangles with colors are already there, and you have to fill in those small, vertical rectangles.

example: row 30: the non-hatched region (between the two green regions) requires a ballast every 1200 length units; while the red region on row 29 requires a ballast every 600 units.

Right?

 

What do those colored rectangles look like? Are those blocks? Is this hatch filled in manually or is there some button (visibility parameter) to change the color of the hatch? Do they contain an attribute with the spacing value ? ... Anything like that?

 

When i start drawing I only have the roof layout. I have to put the panels on the roof layout (the panels are blocks), and then colour it to our engineering standards. The hatched colouring is a block, so are the panels (each colour, red, blue green yellow have their own block and own layer). The small rectangles are ballasts and those are blocks as well. Each ballast block will have its own spacing which is dependent on the zone it is it (both front and rear block have different spacing). ex. yellow zone could have 800mm rear 950mm front, blue 1125mm front 1875mm rear.

Link to comment
Share on other sites

I think this is about what you need.

Can you arrange your blocks to have the items I'm reading?

Or what exactly do you have; what do your blocks look like?

 

It works for 1 row (at a time); I expect each row to be horizontal (same y-point); you can always rotate everything later.

 

--------

; load extra recources
(vl-load-com)

(defun ballast ( / blocks x_values sorted_indexes block spacing x_pointer lft rgt width_panel y_value)
 
 (princ "\nSelect the solar panel blocks (cross select)")
 (setq 
   i 0
   spacing 0.0
   x_pointer 0.0                                                       ;; x_pointer is like the needle of a turn table.  We set it to the x of the left panel block, then it only increases untill we get to the right panel 
   x_values (list)
   blocks (ssget ":N"     ;; client makes a cross select
     (list 
       (cons 0 "INSERT")
     )
   )
 )
 ;; we read the y-value of the first panel
 (setq y_value (nth 2 (assoc 10 (entget (ssname blocks 0)))  ))
 
 ;; we make an array (list) containing the x-value of the insert point 
 (repeat (sslength blocks)
   (setq x_values (append x_values (list
     (cadr (assoc 10 (entget (ssname blocks i))))  ;; returns the insert point, x-value
   )))
   (setq i (+ i 1))
 )
 
 ;; we sort this last list, from left to right.  sorted_indexes contains the index (to be used in the "nth" and "ssname" function  )
 (setq sorted_indexes (vl-sort-i x_values 
   (function (lambda (e1 e2) (< e1 e2)))
 ))
 ;; we set x_pointer to the x-value of the left panel
 (setq x_pointer (nth (nth 0 sorted_indexes) x_values))
 ;; calculate the width of the panel
 
 (setq width_panel (- (nth (nth 1 sorted_indexes) x_values) (nth (nth 0 sorted_indexes) x_values) ))
 
 ;; we loop over sorted_indexes.
 (setq i 0)
 (repeat (length sorted_indexes)
   (setq block (VLAX-ENAME->VLA-OBJECT (ssname blocks (nth i sorted_indexes))))
   ;; set left and right point of the panel
   (setq 
     lft (nth (nth i sorted_indexes) x_values)
     rgt (+ lft width_panel)
   ) 
   ;; we look for the "SPACING" attribute
   (foreach Attribute (vlax-invoke Block "GetAttributes")
     (if (= (vla-get-TagString Attribute) "SPACING")
       (progn
         (setq spacing (atof (vla-get-TextString Attribute)))
         ;;
         (while (< x_pointer rgt)
           (progn
             (AT:InsertBlock "ballast" (list x_pointer y_value) 1.0 1.0 0.0)
             (setq x_pointer (+ x_pointer spacing))
           )
         )
         (princ "\n")
       )
     )
   )
   
   (setq i (+ i 1))
 )
)

;;; Insert block into drawing
;;; #Name - name of block
;;; #InsPt - insert point
;;; #XScale - block X scale
;;; #YScale - block Y scale
;;; #Rot - block rotation
;;; Alan J. Thompson, 04.21.09
(defun AT:InsertBlock (#Name #InsPt #XScale #YScale #Rot)
 (if (or (tblsearch "block" #Name)
         (findfile #Name)
     ) ;_ or
   (vla-insertblock
     ((if (eq (getvar "cvport") 1)
        vla-get-paperspace
        vla-get-modelspace
      ) ;_ if
       (vla-get-ActiveDocument
         (vlax-get-acad-object)
       ) ;_ vla-get-ActiveDocument
     )
     (vlax-3d-point #InsPt)
     #Name
     #XScale
     #YScale
     #XScale
     #Rot
   ) ;_ vla-insert-block
 ) ;_ if
) ;_ defun


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun c:ballast ( / )
 (ballast)
 (princ)
)

spacing.dwg

Link to comment
Share on other sites

This is exactly what I was thinking of. Im glad it is possible. It works great in your test drawing which you have provided however in my drawings I am unable to make it work. Maybe i am using incorrect blocks, or attributes? I have attached a sample layout with blocks I would typically use.

 

test.dwg

Link to comment
Share on other sites

- My code requires a block called "ballast". This is the block that will be inserted.

 

You can change this line, change "ballast" into your block name.

(AT:InsertBlock "ballast" (list x_pointer y_value) 1.0 1.0 0.0)

 

- The panel block (block name does not matter) requires an attribute with the tag: "SPACING"; the contents has to be the spacing value.

 

I don't have Autocad installed here, I will look at your file tomorrow.

Link to comment
Share on other sites

I made a change to the block name in the routine and it worked just like it did in your test. That being said, there is still one issue. The panels have a row of ballast blocks in the rear and the front. Both of which have different spacings. Is there a way to have two sets of attributes per panel?

Link to comment
Share on other sites

I added an attribute "BACK" to the panel.

 

The back ballasts come 200 mm from the bottom; the top ballasts come 600 mm from the bottom.

You can change this with these commands:

(+ 200.0 y_value) and (+ 600.0 y_value)

 

The code is now this:

;; @file: draw ballasts on solar panels, depending on the properties of the roof regions
;; @author: Emmanuel Delay - emmanueldelay@gmail.com
; load extra recources
(vl-load-com)

(defun ballast ( / blocks x_values sorted_indexes block spacing x_pointer x_pointer_back lft rgt width_panel y_value)
 
 (princ "\nSelect the solar panel blocks (cross select)")
 (setq 
   i 0
   spacing 0.0
   x_pointer 0.0                                                       ;; x_pointer is like the needle of a turn table.  We set it to the x of the left panel block, then it only increases untill we get to the right panel 
   x_pointer_back 0.0
   x_values (list)
   blocks (ssget ":N"     ;; client makes a cross select
     (list 
       (cons 0 "INSERT")
     )
   )
 )
 ;; we read the y-value of the first panel
 (setq y_value (nth 2 (assoc 10 (entget (ssname blocks 0)))  ))
 
 ;; we make an array (list) containing the x-value of the insert point 
 (repeat (sslength blocks)
   (setq x_values (append x_values (list
     (cadr (assoc 10 (entget (ssname blocks i))))  ;; returns the insert point, x-value
   )))
   (setq i (+ i 1))
 )
 
 ;; we sort this last list, from left to right.  sorted_indexes contains the index (to be used in the "nth" and "ssname" function  )
 (setq sorted_indexes (vl-sort-i x_values 
   (function (lambda (e1 e2) (< e1 e2)))
 ))
 ;; we set x_pointer to the x-value of the left panel
 (setq x_pointer (nth (nth 0 sorted_indexes) x_values))
 (setq x_pointer_back (nth (nth 0 sorted_indexes) x_values))  ;; same for the back pointer
 
 ;; calculate the width of the panel
 
 (setq width_panel (- (nth (nth 1 sorted_indexes) x_values) (nth (nth 0 sorted_indexes) x_values) ))
 
 ;; we loop over sorted_indexes.
 (setq i 0)
 (repeat (length sorted_indexes)
   (setq block (VLAX-ENAME->VLA-OBJECT (ssname blocks (nth i sorted_indexes))))
   ;; set left and right point of the panel
   (setq 
     lft (nth (nth i sorted_indexes) x_values)
     rgt (+ lft width_panel)
   ) 

   ;; we look for the "SPACING" attribute
   (foreach Attribute (vlax-invoke Block "GetAttributes")
     (if (= (vla-get-TagString Attribute) "SPACING")
       (progn
         (setq spacing (atof (vla-get-TextString Attribute)))
         ;;
         (while (< x_pointer rgt)
           (progn
             (AT:InsertBlock "ballast" (list x_pointer (+ 600.0 y_value)) 1.0 1.0 0.0)
             (setq x_pointer (+ x_pointer spacing))
           )
         )
         (princ "\n")
       )
     )
   )

   ;; we look for the "BACK" attribute
   (foreach Attribute (vlax-invoke Block "GetAttributes")
     (if (= (vla-get-TagString Attribute) "BACK")
       (progn
         (setq spacing (atof (vla-get-TextString Attribute)))
         ;;
         (while (< x_pointer_back rgt)
           (progn
             (AT:InsertBlock "ballast" (list x_pointer_back (+ 200.0 y_value)) 1.0 1.0 0.0)
             (setq x_pointer_back (+ x_pointer_back spacing))
           )
         )
         (princ "\n")
       )
     )
   )
   
   (setq i (+ i 1))
 )
)

;;; Insert block into drawing
;;; #Name - name of block
;;; #InsPt - insert point
;;; #XScale - block X scale
;;; #YScale - block Y scale
;;; #Rot - block rotation
;;; Alan J. Thompson, 04.21.09
(defun AT:InsertBlock (#Name #InsPt #XScale #YScale #Rot)
 (if (or (tblsearch "block" #Name)
         (findfile #Name)
     ) ;_ or
   (vla-insertblock
     ((if (eq (getvar "cvport") 1)
        vla-get-paperspace
        vla-get-modelspace
      ) ;_ if
       (vla-get-ActiveDocument
         (vlax-get-acad-object)
       ) ;_ vla-get-ActiveDocument
     )
     (vlax-3d-point #InsPt)
     #Name
     #XScale
     #YScale
     #XScale
     #Rot
   ) ;_ vla-insert-block
 ) ;_ if
) ;_ defun


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun c:ballast ( / )
 (ballast)
 (princ)
)

spacing.dwg

Edited by Emmanuel Delay
Link to comment
Share on other sites

One more issue than I'm sure this is solved. When I changed the name of the ballast block to "Ballast Block" which is the name of the block we use, I get an "invalid table function argument(s): "Ballast Block" "Ballast Block". When i changed the name in the first lsp you sent me, it works fine with my block, but not with the routine which has both rear and front spacing. Could you please shed some light on this. I attached both of the files. Maybe you can see something I cannot. Sorry I have never really done lsp routines before. Im sure this is an easy fix which I just can not see.Thank you.ballast.lsp

ballast2.lsp

Link to comment
Share on other sites

No problem.

 

It's line 101 (of ballast2.lsp).

 

  (if (or 
     (tblsearch "Ballast Block" #Name)
     (findfile #Name)
   ) ;_ or

should be

 

  (if (or 
     (tblsearch "BLOCK" #Name)
     (findfile #Name)
   ) ;_ or

Link to comment
Share on other sites

No problem.

 

It's line 101 (of ballast2.lsp).

 

  (if (or 
     (tblsearch "Ballast Block" #Name)
     (findfile #Name)
   ) ;_ or

should be

 

  (if (or 
     (tblsearch "BLOCK" #Name)
     (findfile #Name)
   ) ;_ or

 

So that last line where it says BLOCK is basically telling it to insert a block correct? Just trying to understand this better.

 

This works perfectly now that I have changed it. It will reduce my drafting time, and more importantly user errors significantly. Thank you.

Link to comment
Share on other sites

Autocad has a list (a table) of all the blocks, layers ... that are available.

They might or might not have an instance drawn on the dwg, but they are available.

 

tblsearch searches in that list. So, that would also be the function you need to see if you can set a layer

(tblsearch "LAYER" "MyLayer")

 

------

Let me tell you a a few things about LISP.

 

LISP has no operators.

 

Most programming languages do something like this:

 

a = b + 15

LISP does

(setq a (+ b 15))

 

- Opening a "(" means you start a function

"=" and "+" are just functions; in other languages they are operators; so they don't look like functions.

 

----

LISP looks most like javascript.

That is to say: javascript was written to behave like LISP, but look like JAVA or C.

 

You can pass anything to any function. You can pass the definition of a function to another function;

you have anonimous functions: lambda.

 

example

 (setq sorted_indexes (vl-sort-i x_values 
   (function (lambda (e1 e2) (< e1 e2)))
 ))

 

In javascript this would be something like

sorted_indexes.sort(function(a, b) {
 return (a < b);
});

 

So you can pass an anonimous function, each pair that is evaluated passes through that function.

In (most) other languages you can't do that.

 

Both in LISP and javascript there are no classes, no interfaces ... the function does it all.

Edited by Emmanuel Delay
Link to comment
Share on other sites

  • 2 months later...

Let me just say I am very happy with this routine it works perfect, however I am having some troubles trying to update it. I cannot, and do not know how, to change where the routine inserts the blocks via the x coordinate. Also I am wondering as to why the block is grabbing the attribute from the next area as opposed to the area its in. Thanks in advance.

Link to comment
Share on other sites

  • 1 year later...

Hi Experts,

 

I am looking for a LISP routine which user defines an area (rectangle) and a block and 2 numbers x & y (array dimension) and the the routine place the amount of blocks inside the rectangle.

NB: the distance of block-edge of rectangle shall be distance of block-block divided by 2.

I need to use it to place an amount of lighting fixtures. lights must be away from walls half distance of light to light.

 

Thank you all.

Link to comment
Share on other sites

@Chemra

 

 

I'm not 100% sure what exactly you want, this is how I understand it:

 

Let's say you have a room: 12m (x-axis), 6m (y-axis), and you want 3 lamps along the x-axis, 2 lamps along the y-axis

This results in 6 lamps, inserted at (2.0 1.5), (2.0 4.5), (6.0 1.5), (6.0 4.5), (10.0 1.5), (10.0 4.5)

my idea is: I divide the room in 6 equal rectangles, each rectangle has a lamp in the middle of it.

 

 

 

Is that what you have in mind?

 

-----

 

Anyway, here is what I guess you want.

 

Included: 2 test functions, commands: "TEST" & "TEST2" (requires a block called "LAMP", feel free to change this value)

 

 

;; @see http://www.cadtutor.net/forum/showthread.php?88364-block-spacing-lisp&p=667173&viewfull=1#post667173
;; I am looking for a LISP routine which user defines an area (rectangle) and a block and 2 numbers x & y (array dimension) 
;;   and the the routine place the amount of blocks inside the rectangle.
;; NB: the distance of block-edge of rectangle shall be distance of block-block divided by 2.
;; I need to use it to place an amount of lighting fixtures. lights must be away from walls half distance of light to light.

(vl-load-com)

;;; Insert block into drawing
;;; #Name - name of block
;;; #InsPt - insert point
;;; #XScale - block X scale
;;; #YScale - block Y scale
;;; #Rot - block rotation
;;; Alan J. Thompson, 04.21.09
(defun AT:InsertBlock (#Name #InsPt #XScale #YScale #Rot)
 (if (or (tblsearch "block" #Name)
         (findfile #Name)
     ) ;_ or
   (vla-insertblock
     ((if (eq (getvar "cvport") 1)
        vla-get-paperspace
        vla-get-modelspace
      ) ;_ if
       (vla-get-ActiveDocument
         (vlax-get-acad-object)
       ) ;_ vla-get-ActiveDocument
     )
     (vlax-3d-point #InsPt)
     #Name
     #XScale
     #YScale
     #XScale
     #Rot
   ) ;_ vla-insert-block
 ) ;_ if
) ;_ defun


;; inserts a block.  This might be a function where you perform extra stuff, like fill attributes
(defun insert_block (blockname x y / )
 (AT:InsertBlock blockname (list x y) 1.0 1.0 0.0)
)

;; main function
(defun fill_rectangle ( p1 p2 blockname cols rows / segment_x segment_y x y)
 (setq segment_x                ;; (x2 - x1) / cols
   (/ (- (nth 0 p2) (nth 0 p1)) cols)
 )
 (setq segment_y
   (/ (- (nth 1 p2) (nth 1 p1)) rows)
 )
 (setq 
   x 0
   y 0
 )
 (repeat cols
   (setq y 0)
   (repeat rows
     (insert_block 
        blockname 
       (+ (nth 0 p1) (/ segment_x 2 ) (* segment_x x ))  ;; insert point (x) + half segment length + X number full segment lengths
       (+ (nth 1 p1) (/ segment_y 2 ) (* segment_y y ))
     )
     (setq y (+ y 1))
   )
   (setq x (+ x 1))
 )
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; test function with hardcoded values, as example.
;; This requires a block "LAMP"
(defun c:test2 ( / p1 p2)
 (fill_rectangle
   (list 0.0 0.0)
   (list 12.0 6.0)
   "LAMP"
   3
   2
 )
 (princ)
)


;; test function with user selected values.
(defun c:test ( / p1 p2)
 (fill_rectangle
   
   ;; user defines an area (rectangle)
   (setq p1 (getpoint     "\nPoint 1, bottom-left: "))
   (setq p2 (getcorner p1 "\nPoint 2, top-right: "))
   
   ;; and a block
   (getstring "\nBlock name: ")
   
   ;; and 2 numbers x & y (array dimension)
   (getint "\nX (number of colums) ")
   (getint "\nY (number of rows) ")
   
 )
 (princ)
)

Edited by Emmanuel Delay
Link to comment
Share on other sites

Hi Emmanuel,

 

I was trying to change the way of getting the block. I tried entsel, however, i still getting error, too many arguments. Any advice?

Thanks

 

@Chemra

 

 

I'm not 100% sure what exactly you want, this is how I understand it:

 

Let's say you have a room: 12m (x-axis), 6m (y-axis), and you want 3 lamps along the x-axis, 2 lamps along the y-axis

This results in 6 lamps, inserted at (2.0 1.5), (2.0 4.5), (6.0 1.5), (6.0 4.5), (10.0 1.5), (10.0 4.5)

my idea is: I divide the room in 6 equal rectangles, each rectangle has a lamp in the middle of it.

 

 

 

Is that what you have in mind?

 

-----

 

Anyway, here is what I guess you want.

 

Included: 2 test functions, commands: "TEST" & "TEST2" (requires a block called "LAMP", feel free to change this value)

 

 

;; @see http://www.cadtutor.net/forum/showthread.php?88364-block-spacing-lisp&p=667173&viewfull=1#post667173
;; I am looking for a LISP routine which user defines an area (rectangle) and a block and 2 numbers x & y (array dimension) 
;;   and the the routine place the amount of blocks inside the rectangle.
;; NB: the distance of block-edge of rectangle shall be distance of block-block divided by 2.
;; I need to use it to place an amount of lighting fixtures. lights must be away from walls half distance of light to light.

(vl-load-com)

;;; Insert block into drawing
;;; #Name - name of block
;;; #InsPt - insert point
;;; #XScale - block X scale
;;; #YScale - block Y scale
;;; #Rot - block rotation
;;; Alan J. Thompson, 04.21.09
(defun AT:InsertBlock (#Name #InsPt #XScale #YScale #Rot)
 (if (or (tblsearch "block" #Name)
         (findfile #Name)
     ) ;_ or
   (vla-insertblock
     ((if (eq (getvar "cvport") 1)
        vla-get-paperspace
        vla-get-modelspace
      ) ;_ if
       (vla-get-ActiveDocument
         (vlax-get-acad-object)
       ) ;_ vla-get-ActiveDocument
     )
     (vlax-3d-point #InsPt)
     #Name
     #XScale
     #YScale
     #XScale
     #Rot
   ) ;_ vla-insert-block
 ) ;_ if
) ;_ defun


;; inserts a block.  This might be a function where you perform extra stuff, like fill attributes
(defun insert_block (blockname x y / )
 (AT:InsertBlock blockname (list x y) 1.0 1.0 0.0)
)

;; main function
(defun fill_rectangle ( p1 p2 blockname cols rows / segment_x segment_y x y)
 (setq segment_x                ;; (x2 - x1) / cols
   (/ (- (nth 0 p2) (nth 0 p1)) cols)
 )
 (setq segment_y
   (/ (- (nth 1 p2) (nth 1 p1)) rows)
 )
 (setq 
   x 0
   y 0
 )
 (repeat cols
   (setq y 0)
   (repeat rows
     (insert_block 
        blockname 
       (+ (nth 0 p1) (/ segment_x 2 ) (* segment_x x ))  ;; insert point (x) + half segment length + X number full segment lengths
       (+ (nth 1 p1) (/ segment_y 2 ) (* segment_y y ))
     )
     (setq y (+ y 1))
   )
   (setq x (+ x 1))
 )
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; test function with hardcoded values, as example.
;; This requires a block "LAMP"
(defun c:test2 ( / p1 p2)
 (fill_rectangle
   (list 0.0 0.0)
   (list 12.0 6.0)
   "LAMP"
   3
   2
 )
 (princ)
)


;; test function with user selected values.
(defun c:test ( / p1 p2)
 (fill_rectangle
   
   ;; user defines an area (rectangle)
   (setq p1 (getpoint     "\nPoint 1, bottom-left: "))
   (setq p2 (getcorner p1 "\nPoint 2, top-right: "))
   
   ;; and a block
   (getstring "\nBlock name: ")
   
   ;; and 2 numbers x & y (array dimension)
   (getint "\nX (number of colums) ")
   (getint "\nY (number of rows) ")
   
 )
 (princ)
)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...