auto lisp to insert a block, a distance from the middle point of all selected lines?

auto lisp to insert a block, a distance from the middle point of all selected lines?

mkroll9in5in
Enthusiast Enthusiast
3,073 Views
32 Replies
Message 1 of 33

auto lisp to insert a block, a distance from the middle point of all selected lines?

mkroll9in5in
Enthusiast
Enthusiast

I have a part with different edges, in different layers. I can write the macro to isolate the individual layers. All I need is a lisp to place a block by name, at a set distance, from all selected line segment midpoints. In the lisp I wish to be able to set the block name and offset distance. The trick is what side will it offset on. how will it know what is the inside of the part?  if need be, both sides.

0 Likes
3,074 Views
32 Replies
Replies (32)
Message 2 of 33

Moshe-A
Mentor
Mentor

@mkroll9in5in hi,

 

Please post a sample drawing 

 

Moshe

 

 

 

0 Likes
Message 3 of 33

mkroll9in5in
Enthusiast
Enthusiast

The letters located about midpoint, but offset into the inside portion of the box. I need a lisp that I can easily manipulate what (block file name) it is importing and how far the offset is, as that might change based on the size of the block. As far as isolating that layer to apply the LISP, I can do that in a macro up front. In my macro I will isolate the red layer, then the lisp will apply said "block" at said "distance" from the "midpoint" of "selected line segments". Then I can tweak it for the different blocks and offsets. The part I see that is going to be hard is, how does it know what side of the line to put it on?

mkroll9in5in_1-1683491845440.png

 

0 Likes
Message 4 of 33

mkroll9in5in
Enthusiast
Enthusiast

I have a better idea. I start the lisp, it asks me to select the line segment, then a side to offset, then it sets a (predetermined block) at a (predetermined distance) that is written within the lisp. This way I can mod the lisp by changing its (command to activate it) based on the block, (what block file) it calls into play, and (how far to offset) on that (selected side). If it can be done to do all line segments in the drawing at one time would be better, but again how do you control the side it puts it on automatically? A part may have up to 4 different layers of lines going around in a closed loop. raw, polish, profile, seam. There can be up to 10 parts in a drawing. Trying to find a faster way to label all the edges with a block in a single click. At the moment I have custom buttons with custom icons showing an image of the block. It does a macro inserting that block at the end of your cursor to be pasted multiple times. It works, but I know there is a better way.

0 Likes
Message 5 of 33

Moshe-A
Mentor
Mentor

@mkroll9in5in 

 

the only way i know to automatically determine the inside of a 'part' is to create it as closed polyline.

 

Moshe

 

 

 

0 Likes
Message 6 of 33

Kent1Cooper
Consultant
Consultant

@mkroll9in5in wrote:

.... all line segments in the drawing at one time would be better, but again how do you control the side it puts it on automatically? ....


Are "all line segments" the only line segments in the drawing?  That is, is the shape you want to know the inside of the entire drawing?  If so, and if shapes are not too convoluted, something could probably be determined based on the middle of the drawing's extents.

Kent Cooper, AIA
0 Likes
Message 7 of 33

mkroll9in5in
Enthusiast
Enthusiast

A drawing could have 1-20 2D parts, each of the parts have up to 4 layers of line segments.

With a macro I can shut off all layers, then turn on one of those layers isolating that layer and its segments in question.

I imagine at this point running a LISP within the macro to label those isolated line segments with a

preset (block) in this case the 2 LISP's could be PLABEL and MLABEL at the (midpoint) with a preset (offset) on all (selected line segments), the LISP would prompt me to manually select the segments in question,

then continuing back into the macro to turn off all layers once again, to isolate the next layer running a similar LISP,

but it calls for a different block with a different offset for a different set of line segments.

mkroll9in5in_0-1683550888979.png

 

0 Likes
Message 8 of 33

Moshe-A
Mentor
Mentor

@mkroll9in5in ,

 

Now i'm starting to understand you, you are talking about inserting a text or attribute block inside a geometric (part)? and the attribute value is fixed according the layer?

 

the solution to this is very simple 😀

lisp 1: select all lines and insert the attribute block.

lisp2 : will fix the block position at each one went out.

 

or 

 

Another idea is to run the command on each 'part' (instead on all of them). the program on the fly will gather all lines to a close polyline (as long all lines intersect) and insert the blocks attribute straight in the right place (outside).

 

i prefer the later version, post sample dwg

 

Moshe

 

 

 

0 Likes
Message 9 of 33

Moshe-A
Mentor
Mentor

@mkroll9in5in ,

 

give this ALIATT command go 😀

 

This is the second option the one to select 'part' lines (which must be intersect in order to be able to close as pline)

the program starts with declaring some constants (lines 58-61)

(setq TAGSZ 0.30) ; this is used as tag height
(setq DIMSCL (getvar "dimscale")) ;  this is used for scaling the tag

 

next is your main data, a list of dotted pair => (layer_color . "tag value")
(setq DATALST '((1 . "P") (3 . "M") (200 . "R"))) ; add here pairs of color + tag value

 

next (ssget) select 'part' lines (line 63)

next the lines are duplicate (lines 65-70)

next PEDIT is invoked to join and close as pline (line 71)

next the pline is converted to a REGION to get it's centroid point (lines 72-76)

next the region is deleted.

next the tags is added along the lines inside 'part' (lines 78-86)

 

i know you wanted the tag to be attribute block but you did not post your block and as a matter of fact i do not see any reason why this should be (it's 1 text)

 

study the local functions.

 

Note: if a layer color is not declare in DATALST, a value of "N/A" not available is returned.

 

enjoy

Moshe

 

 

(vl-load-com) ; load activex support

; align attribute
(defun c:aliatt (/ beside_edge readable_angle get_tag_value ; local functions
		   TAGSZ DIMSCL DATALST ; constant
		   ss0 ss1 ent AcDbRegion elist c0 c1 p10 p11 p12 p13)

 ; return beside edge length 
 (defun beside_edge (/ trig_angle ; local function
		       ang0 ang1 ang2 cx)

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

   (if (> a2 pi)
    (setq a2 (- (* pi 2) a2))
    a2 
   )
  ); trig_angle
   
  (setq ang0 (angle p10 p11))
  (setq ang1 (angle p10 c0))
  (setq ang2 (trig_angle ang0 ang1))
  (setq cx (distance p10 c0))
   
  (* (cos ang2) cx) ; return
 ); beside_edge

 ; return text readable angle
 (defun readable_angle (p0 p1 / a0)
  (setq a0 (angle p0 p1))
  (if (and (> a0 (* pi 0.5)) (< a0 (* pi 1.5)))
   (+ a0 pi)
   a0
  )
 ); readable_angle 
  
 ; return tag value
 (defun get_tag_value (e / dotpair color)
  (if (not (setq color (cdr (assoc '62 (tblsearch "layer" (cdr (assoc '8 e)))))))
   "N/A"
   ; else 
   (if (not (setq dotpair (assoc color DATALST)))
    "N/A"
    (cdr dotpair)
   ); if
  ); if
 ); get_tag_value

 ; here start c:aliatt
 (setvar "cmdecho" 0)
 (command "._undo" "_begin")
  
 ; define some constants
 (setq TAGSZ 0.30) ; tag size
 (setq DIMSCL (getvar "dimscale"))
 (setq DATALST '((1 . "P") (3 . "M") (200 . "R")))
  
 (if (setq ss0 (ssget '((0 . "line"))))
  (progn
   (setq ent (entlast))
   (command "._copy" "_si" ss0 "0,0,0" "0,0,0")
   (setq ss1 (ssadd))
   (while (setq ent (entnext ent))
    (ssadd ent ss1) 
   ); while
   (command "._pedit" (ssname ss1 0) "_yes" "_join" "_si" ss1 "")
   (command "._region" "_si" (entlast))
   (setq AcDbRegion (vlax-ename->vla-object (entlast)))
   (setq c0 (vlax-safearray->list (vlax-variant-value (vla-get-centroid AcDbRegion))))
   (vla-delete AcDbRegion)
   (vlax-release-object AcDbRegion)

   (foreach ent (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss0)))
    (setq elist (entget ent))
    (setq p10 (cdr (assoc '10 elist)))
    (setq p11 (cdr (assoc '11 elist)))
    (setq c1 (polar p10 (angle p10 p11) (beside_edge)))
    (setq p12 (mapcar (function (lambda (x0 x1) (/ (+ x0 x1) 2))) p10 p11))
    (setq p13 (polar p12 (angle c1 c0) (* 1.5 TAGSZ DIMSCL)))
    (command "._text" "_middle" "_none" p13 (* TAGSZ DIMSCL)(angtos (readable_angle p10 p11) 0 4) (get_tag_value elist))
   ); foreach
  ); progn
 ); if

 (command "._undo" "_end") 
 (setvar "cmdecho" 1)
 
 (princ) 
); c:aliatt

 

0 Likes
Message 10 of 33

Sea-Haven
Mentor
Mentor

A maybe pick point inside make a bpoly this is always CCW, so know which way is inside, then using ssget pt repeat for each pair of line segments ie mid point of the 2 points, get the layer and add the correct text or block. 

 

Try this labels layer at moment as no sample dwg so could be better.

 

 

 

 

 

See post 21 for latest version.

 

 

 

 

 

 

0 Likes
Message 11 of 33

mkroll9in5in
Enthusiast
Enthusiast

The first image was the results of (aliatt).
I was able to start the lisp, select everything, hit enter, and this is what I got.
unfortunately some of the labels were on the outside and it reset font size to 0.3.
I work more around 2.0. I'm sure that is an easy fix.

I added something a little more of what I'm looking for in the second image.

I would much prefer to use blocks rather than attributes in the case I wish to insert a geometry based blocks that looks like text, but are lines and arcs. (for laser projector on a cnc machine that can not project text).

 

I have to number each part in a particular order, so that must be done manually. I currently have a macro set up to import consecutive blocks files of numbers 1-100 (this was before AutoCAD LT added LISP) where I start the macro and it inserts block 1, you place it in the middle of the part, then it inserts block 2...
(^C^C_-layer;s;0;;_-insert;1A;\;;;_x;l;_-insert;2A;\;;;_x;l;)
maybe a lisp for the labeling can be tide into this macro?
(^C^C_-layer;s;0;;_-insert;1A;\;;;_x;l;_LISP;_-insert;2A;\;;;_x;l;_LISP;)

The other guys answer was interesting with the use of bpoly and selecting the inner area of each part. Maybe this can be teamed up with numbering each part including areas with seams.
So think of this, start the lisp, i select the inside of each part. as i do it places block1 where I click in that area then "magically"  it labels all of the edges in that area based on its layer, then it moves to insert block2... repeat for each part.

 

mkroll9in5in_0-1683856508261.png

mkroll9in5in_1-1683856664444.png

 

0 Likes
Message 12 of 33

mkroll9in5in
Enthusiast
Enthusiast

The first image was the results of (wow).
I was able to start the lisp, was prompted to select the area of each part. It proceeded to label each line segment by its payer name. Very cool, but not quite there. There also seem to be an issue with what side of the line it put things on

I added something a little more of what I'm looking for in the second image.

I would much prefer to use blocks rather than attributes in the case I wish to insert a geometry based blocks that looks like text, but are lines and arcs. (for laser projector on a cnc machine that can not project text).

 

I have to number each part in a particular order, so that must be done manually. I currently have a macro set up to import consecutive blocks files of numbers 1-100 (this was before AutoCAD LT added LISP) where I start the macros and it inserts block 1, you place it in the middle of the part, then it inserts block 2...
(^C^C_-layer;s;0;;_-insert;1A;\;;;_x;l;_-insert;2A;\;;;_x;l;)
maybe a lisp for the labeling can be tide into this macro?
(^C^C_-layer;s;0;;_-insert;1A;\;;;_x;l;_LISP;_-insert;2A;\;;;_x;l;_LISP;)


I found your answer interesting with the use of bpoly and selecting the inner area of each part. Maybe this can be teamed up with numbering each part including areas with seams.
So think of this, start the lisp, i select the inside of each part. As I do it places block1 where I click in that area then "magically"  it labels all of the edges in that area based on its layer, then it moves to insert block2... repeat for each part.

 

mkroll9in5in_0-1683858546905.png

 

mkroll9in5in_1-1683856664444.png

0 Likes
Message 13 of 33

Moshe-A
Mentor
Mentor

@mkroll9in5in hi,

 

On message #2 i asked you to post your sample dwg. the fact that you are posing an image does not help you.

are you running the lisp on image, no - right? then how do you think can we?

 

Moshe

 

0 Likes
Message 14 of 33

mkroll9in5in
Enthusiast
Enthusiast

I have been doing AutoCAD LT for a long time and I'm fairly good at building toolbars and macros, as LT didn't have LISP at the time. So I learned to make do, but I have scratched the surface seeing some pretty neat things that can be done with LISP.

When I first asked this question about auto labeling inner midpoints of line segments, I wanted to keep it simple and not ask too much and see if I can find a small LISP to add to my macro skills that would label the midpoint of a group of selected lines with a block offset to a particular side. My idea was to would mod that LISP in to multiple LISP's, each taking care of one layer of lines, stringed together in a macro to isolate that layer prior to running the LISP. Now seeing what you and the other guy came up with I feel something more and bigger can be done here.

As I mentioned, I have to manually number each part anyways in order of install, I feel the act of selecting the inner area of each part 1,2,3... leaves an opportunity to take advantage of that act to number, auto edge label, and dimension each segment of each part. This would eliminate upwards of 15 click per part. (as any CAD person should be, reduce the clicks required to get from A to B). I feel text or attributes for the edge labels and numbered parts would be easier to code, but as I mentioned, being able to have it insert geometry based blocks would be more ideal for CNC laser projectors that can only project line segments.

 

This is only my second week of messing with LISP and I have so far "circle none closed polyline endpoints" the macro isolates the 5 layers of geometry (freezing all others) and polylines everything remaining, then the LISP built inside that macro circles all open endpoints revealing the breaks in the parts from bad templater and digitizeer, very cool, "pop-up sqft", "pop-up linear foot and inches" also using macros to freeze all irrelevant layers prior to running the LISP that is tided into the tail end of that macro. Sorry for the confusion. I cannot learn LISP soon enough.

0 Likes
Message 15 of 33

Sea-Haven
Mentor
Mentor

I will check why text does not go inwards.

0 Likes
Message 16 of 33

mkroll9in5in
Enthusiast
Enthusiast

I just took a quick look at it, only the top and bottom put it on the out side.  Left and right was good.

0 Likes
Message 17 of 33

komondormrex
Mentor
Mentor

so you are using autocad lt?

0 Likes
Message 18 of 33

mkroll9in5in
Enthusiast
Enthusiast

2024 LT with LISP, yea buddy. I only do 2d cnc counter top work. the full version is 3x$ overkill. Not having LISP I was on the fence about to cave, now that is no longer the case. I say I am pretty good customizing my ribbons and creating my own buttons with macro strings a mile long in some cases.
I have since rephrased this question now seeing possibilities from this thread. I included a sample drawing there as well.
"macro or lisp to auto insert blocks at center of part and just inside the midpoint of surrounding line segments based on layer or pin color"

0 Likes
Message 19 of 33

Sea-Haven
Mentor
Mentor

I have updated the code above but need a sample dwg to test and finish it properly.

0 Likes
Message 20 of 33

komondormrex
Mentor
Mentor
0 Likes