Jump to content

Erasing tiny arcs


Happy Hobbit

Recommended Posts

Please do not rewrite the following lisp.

We import drawings from about a dozen manufacturers of electical equipment for use in our mechanical assembly drawings.

Several of these manufacturers supply their drawing converted from a non AutoCAD package & these drawings often have loads of unwanted tiny arcs in them.

 

Therefore I decided to write a lisp to delete them as follows:

(defun C:SmARC ( / ents ss)
(setq ss (ssget "_A" '((0 . "ARC"))))
 (if ss
   (progn
  (setq i 0)
  (repeat (sslength ss)
	(if (< (* (cdr(assoc 40 (entget(ssname ss i)))) (-(cdr (assoc 51 (entget(ssname ss i)))) (cdr (assoc 50 (entget(ssname ss i))))))1)
	(setq ents (cons(ssname ss i)ents))
	)
    (setq i (1+ i))
 )  
  (if ents
    (princ (strcat "\n "(itoa (length ents))" Small Arcs Found"))
    )
 ); progn
       (princ "\n No Small Arcs Found")
   )    
 ;(command "_erase" ents)
 ;(entdel ents)
 (princ)
)

 

The lisp finds the arcs & filters out the small ones, & prints how many it has found, but I cannot find a way in the lisp to erase them.

(See the two lines coloned off at bottom of lisp)

 

I would prefer not to use vl type functions

 

Do I need another repeat loop to erase them one by one using entdel?

 

- Harry

Link to comment
Share on other sites

As a temporary 'fix' I added the following, replacing the two coloned off lines:

  (setq ss2(ssadd))
(setq n 0)
(repeat (length ents)
(setq ss2 (ssadd (nth n ents)ss2))
(setq n (1+ n))
)
(sssetfirst nil ss2)  

 

Then simply press the delete key!

Link to comment
Share on other sites

As a suggestion, consider that the following line is obtaining the same entity from the selection set and retrieving the same DXF data three times over:

(< (* (cdr(assoc 40 (entget(ssname ss i)))) (-(cdr (assoc 51 (entget(ssname ss i)))) (cdr (assoc 50 (entget(ssname ss i))))))1)

Since these operations need only be performed once, the code would be far more efficient if you assigned such data to local variables, e.g.:

(setq ent (ssname ss i)
     enx (entget ent)
)
(if (< (* (cdr (assoc 40 enx)) (- (cdr (assoc 51 enx)) (cdr (assoc 50 enx)))) 1)

Also, be careful of arcs such as the following:

(entmake
  '(
       (0 . "ARC")
       (10 0 0 0)
       (40 . 1.0)
       (50 . 5.0)
       (51 . 1.0)
   )
)

Link to comment
Share on other sites

(setq ent (ssname ss i)
     enx (entget ent)
)
(if (< (* (cdr (assoc 40 enx)) (- (cdr (assoc 51 enx)) (cdr (assoc 50 enx)))) 1)

 

Wow, that's a neat idea, you mean redefine ent & enx with every 'turn' of the loop?

It certainly make the code easier to understand.

 

Maybe make it even shorter with

(setq enx (entget (ssname ss i)))

?

 

Addendum:

 

Would the variable enx need redefining in the loop? Bearing in mind the index i will increment up

 

I shall try it tomorrow morning

 

I didn't know one could put AutoLISP Functions inside a variable, however it was on my 'try it' list of things to do.

 

The mind boggles with the difficulties it might cause if the variable were global....

Edited by Happy Hobbit
Link to comment
Share on other sites

(setq ent (ssname ss i)
     enx (entget ent)
)
(if (< (* (cdr (assoc 40 enx)) (- (cdr (assoc 51 enx)) (cdr (assoc 50 enx)))) 1)

Wow, that's a neat idea, you mean redefine ent & enx with every 'turn' of the loop?

 

Exactly.

 

Maybe make it even shorter with
(setq enx (entget (ssname ss i)))

 

You could write it this way, however, by defining a variable pointing to the entity name, you can pass the entity name to entdel within the loop to perform the deletion.

 

Would the variable enx need redefining in the loop? Bearing in mind the index i will increment up

 

Yes, otherwise it will always hold the DXF data for the same entity.

 

I didn't know one could put AutoLISP Functions inside a variable

 

Note that the code is simply assigning the values returned by the various AutoLISP functions to arbitrary symbols using setq, we are not storing/redefining functions in any way.

Link to comment
Share on other sites

Further to the above, below are my suggestions for your code, with each expression annotated:

([color=BLUE]defun[/color] c:smarc
   [color=GREEN];; Define function with 'c:' prefix to enable command-line invocation[/color]

   ( [color=BLUE]/[/color] ent enx idx sel )
   [color=GREEN];; Declare symbols whose scope should be local to this function[/color]

   ([color=BLUE]if[/color]
   [color=GREEN];; If the following expression returns a non-nil value[/color]
       ([color=BLUE]setq[/color]
           sel
       [color=GREEN];; Assign the value returned by the following expression to the symbol 'sel' and return this value[/color]
           ([color=BLUE]ssget[/color] [color=MAROON]"_A"[/color] '((0 . [color=MAROON]"ARC"[/color])))
           [color=GREEN];; Automatically retrieve a selection set of all ARC entities residing on thawed layers[/color]
       ) [color=GREEN];; end setq[/color]

       [color=GREEN];; THEN argument for the IF statement:[/color]
       ([color=BLUE]repeat[/color]
       [color=GREEN];; Repeat the enclosed expressions a number of times equal to:[/color]
           ([color=BLUE]setq[/color]
               idx
           [color=GREEN];; Assign the value returned by the following expression to the symbol 'idx' and return this value[/color]
               ([color=BLUE]sslength[/color] sel)
               [color=GREEN];; Return the number of items in the selection set assigned to the variable 'sel'[/color]
           ) [color=GREEN];; end setq[/color]

           ([color=BLUE]setq[/color]
               ent
               [color=GREEN];; Assign the value returned by the following expression to the symbol 'ent'[/color]
               ([color=BLUE]ssname[/color] sel
               [color=GREEN];; Retrieve the entity name at the following index in the selection set 'sel'[/color]
                   ([color=BLUE]setq[/color] idx ([color=BLUE]1-[/color] idx))
                   [color=GREEN];; Decrement the variable 'idx' (as the selection set index is zero-based)[/color]
               ) [color=GREEN];; end ssname[/color]
               enx
               [color=GREEN];; Assign the value returned by the following expression to the symbol 'enx'[/color]
               ([color=BLUE]entget[/color] ent)
               [color=GREEN];; Retrieve the DXF data associated with the entity 'ent' in the drawing database[/color]
           ) [color=GREEN];; end setq[/color]

           ([color=BLUE]if[/color]
           [color=GREEN];; If the following expression returns a non-nil value[/color]
               ([color=BLUE]<[/color]
               [color=GREEN];; Return T if the following expressions are sequentially greater but not equal in magnitude[/color]
               [color=GREEN];; (i.e. return T if the following is less than 1)[/color]
                   ([color=BLUE]*[/color]
                   [color=GREEN];; Return the result of multiplying the values returned by the following expressions[/color]
                   [color=GREEN];; (i.e. return the arc length)[/color]
                       ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 40 enx))
                       [color=GREEN];; Retrieve the arc radius and multiply it by:[/color]
                       ([color=BLUE]rem[/color]
                       [color=GREEN];; Return the remainder when dividing the following value by the 2nd arg (2pi)[/color]
                           ([color=BLUE]+[/color]
                           [color=GREEN];; Return the result of adding the following values[/color]
                               ([color=BLUE]-[/color] ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 51 enx))
                                  ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 50 enx))
                               )
                               [color=GREEN];; Subtract the arc end angle from the arc start angle[/color]
                               [color=GREEN];; (i.e. return the delta angle - this may be negative)[/color]
                               [color=BLUE]pi[/color] [color=BLUE]pi[/color]
                               [color=GREEN];; Add 2pi to ensure the result is positive[/color]
                           ) [color=GREEN];; end +[/color]
                           ([color=BLUE]+[/color] [color=BLUE]pi[/color] [color=BLUE]pi[/color])
                           [color=GREEN];; Return the remainder following division by 2pi[/color]
                           [color=GREEN];; (i.e. restrict the angle to the range 0 <= x < 2pi)[/color]
                       ) [color=GREEN];; end rem[/color]
                   ) [color=GREEN];; end *[/color]
                   1.0
               ) [color=GREEN];; end <[/color]
               
               [color=GREEN];; THEN argument for the IF statement:[/color]
               ([color=BLUE]entdel[/color] ent)
               [color=GREEN];; Set the erase flag for the entity if the arc length is less than 1.0[/color]
               
           ) [color=GREEN];; end if[/color]
       ) [color=GREEN];; end repeat[/color]

       [color=GREEN];; ELSE argument for the IF statement:[/color]
       ([color=BLUE]princ[/color] [color=MAROON]"\nNo arcs found."[/color])
       [color=GREEN];; Inform the user that the selection set was null[/color]
       [color=GREEN];; (i.e. no arcs were found on thawed layers)[/color]
   ) [color=GREEN]; end if[/color]

   ([color=BLUE]princ[/color])
   [color=GREEN];; Suppress the value returned by the last evaluated expression (i.e. the IF expression)[/color]
) [color=GREEN];; end defun[/color]

And here is the above code without comments for clarity:

([color=BLUE]defun[/color] c:smarc ( [color=BLUE]/[/color] ent enx idx sel )
   ([color=BLUE]if[/color] ([color=BLUE]setq[/color] sel ([color=BLUE]ssget[/color] [color=MAROON]"_A"[/color] '((0 . [color=MAROON]"ARC"[/color]))))
       ([color=BLUE]repeat[/color] ([color=BLUE]setq[/color] idx ([color=BLUE]sslength[/color] sel))
           ([color=BLUE]setq[/color] ent ([color=BLUE]ssname[/color] sel ([color=BLUE]setq[/color] idx ([color=BLUE]1-[/color] idx)))
                 enx ([color=BLUE]entget[/color] ent)
           )
           ([color=BLUE]if[/color] ([color=BLUE]<[/color] ([color=BLUE]*[/color] ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 40 enx)) ([color=BLUE]rem[/color] ([color=BLUE]+[/color] ([color=BLUE]-[/color] ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 51 enx)) ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 50 enx))) [color=BLUE]pi[/color] [color=BLUE]pi[/color]) ([color=BLUE]+[/color] [color=BLUE]pi[/color] [color=BLUE]pi[/color]))) 1.0)
               ([color=BLUE]entdel[/color] ent)
           )
       )
       ([color=BLUE]princ[/color] [color=MAROON]"\nNo arcs found."[/color])
   )
   ([color=BLUE]princ[/color])
)

Link to comment
Share on other sites

My $0.05 why make a selection set then delete why not delete as you find them ? Replace your cons line with a delete.

 

Thanks BIGAL, I realised I could do this however it's a bit of an obsession of mine to make the lisp report back how many items it has deleted/modified/copied or whatever. I was getting there, albeit slowly.

Link to comment
Share on other sites

I think I've solved the problem, until someone tells me otherwise...

 

The only trouble is I sort of cobbled it together from multiple sorces the line:

(vlax-curve-getdistAtParam (vlax-ename->vla-object (cdr(car enx))) (vlax-curve-getEndParam (vlax-ename->vla-object (cdr(car enx)))))

Please can somebody explain to me in plain English how it works?

 

Not the

(cdr(car enx))

bit, that's easy

 

The actual code is as follows:

It works very well BTW & will delete small arc, circles, lines, polylines & splines. Also all point entities

(defun c:Delsmbits ( / arcs circ d ent enx idx lin plin pont sel splin ss)
(setq arcs 0
     circ 0
     lin 0
     plin 0
     pont 0
     splin 0
 )
 (initget "0.1 0.25 0.5 1.0")
 (setq frag (cond ((getkword "\nDelete Arcs/Circles/Lines/Polylines/Splines smaller than or equal to: [0.1/0.25/0.5/1.0] <0.5>: "))
                          ("0.5")))
(cond
   ((equal frag "0.1")
 (setq d 0.1)
   )
   ((equal frag "0.25")
 (setq d 0.25)
   )
       ((equal frag "0.5")
 (setq d 0.5)
   )
       ((equal frag "1.0")
 (setq d 1.0)
   )
 );end cond
 
 (if (setq sel (ssget "_A" '((0 . "ARC,CIRCLE,LINE,LWPOLYLINE,SPLINE"))))
       (repeat (setq idx (sslength sel))
           (setq ent (ssname sel (setq idx (1- idx)))
                 enx (entget ent)
           )
(if(<= (vlax-curve-getdistAtParam (vlax-ename->vla-object (cdr(car enx))) (vlax-curve-getEndParam (vlax-ename->vla-object (cdr(car enx))))) d)
  (cond
    ((equal "ARC" (cdr (assoc 0 enx)))	    
(setq arcs (1+ arcs))
(entdel ent)
     )
    ((equal "CIRCLE" (cdr (assoc 0 enx)))	    
(setq circ (1+ circ))
(entdel ent)
     )
    ((equal "LINE" (cdr (assoc 0 enx)))	    
(setq lin (1+ lin))
(entdel ent)
     )
((equal "LWPOLYLINE" (cdr (assoc 0 enx)))	    
(setq plin (1+ plin))
(entdel ent)
     )		    
    ((equal "SPLINE" (cdr (assoc 0 enx)))	    
(setq splin (1+ splin))
(entdel ent)
     )
    );; cond
);; If	
       );; Repeat
   
       ;(princ "\nNo Small stuff found.")
   )
(if (setq ss (ssget "x" '((0 . "POINT"))))
  (progn
    (setq pont (sslength ss))
    (command "_.erase" ss "")
   )
  
  )  
(princ (strcat (itoa arcs) " Arcs, " (itoa lin) " Lines, " (itoa circ) " Circles, " (itoa plin) " Polylines, " (itoa pont) " Points, and " (itoa splin) " Splines " "Deleted"))
   (princ)
)
(vl-load-com)

Link to comment
Share on other sites

(vlax-curve-getdistAtParam (vlax-ename->vla-object (cdr(car enx))) (vlax-curve-getEndParam (vlax-ename->vla-object (cdr(car enx)))))

Please can somebody explain to me in plain English how it works?

 

Firstly, a little-known fact is that the vlax-curve-* functions will accept entity name arguments (and will actually perform faster with enames over vla-objects), therefore, the expression to calculate the length can become:

(vlax-curve-getdistatparam ent (vlax-curve-getendparam ent))

[ Here is an existing example ]

 

As for the 'how it works', have you first read the documentation for each of the curve functions used?

 

http://help.autodesk.com/view/ACD/2015/ENU/?guid=GUID-4684C76F-02F7-4989-AA53-C886E528350A

 

The actual code is as follows:

It works very well BTW & will delete small arc, circles, lines, polylines & splines. Also all point entities

(defun c:Delsmbits ( / arcs circ d ent enx idx lin plin pont sel splin ss)
(setq arcs 0
     circ 0
     lin 0
     plin 0
     pont 0
     splin 0
 )
 (initget "0.1 0.25 0.5 1.0")
 (setq frag (cond ((getkword "\nDelete Arcs/Circles/Lines/Polylines/Splines smaller than or equal to: [0.1/0.25/0.5/1.0] <0.5>: "))
                          ("0.5")))
(cond
   ((equal frag "0.1")
 (setq d 0.1)
   )
   ((equal frag "0.25")
 (setq d 0.25)
   )
       ((equal frag "0.5")
 (setq d 0.5)
   )
       ((equal frag "1.0")
 (setq d 1.0)
   )
 );end cond
 
 (if (setq sel (ssget "_A" '((0 . "ARC,CIRCLE,LINE,LWPOLYLINE,SPLINE"))))
       (repeat (setq idx (sslength sel))
           (setq ent (ssname sel (setq idx (1- idx)))
                 enx (entget ent)
           )
   (if(<= (vlax-curve-getdistAtParam (vlax-ename->vla-object (cdr(car enx))) (vlax-curve-getEndParam (vlax-ename->vla-object (cdr(car enx))))) d)
     (cond
       ((equal "ARC" (cdr (assoc 0 enx)))        
   (setq arcs (1+ arcs))
   (entdel ent)
        )
       ((equal "CIRCLE" (cdr (assoc 0 enx)))        
   (setq circ (1+ circ))
   (entdel ent)
        )
       ((equal "LINE" (cdr (assoc 0 enx)))        
   (setq lin (1+ lin))
   (entdel ent)
        )
   ((equal "LWPOLYLINE" (cdr (assoc 0 enx)))        
   (setq plin (1+ plin))
   (entdel ent)
        )            
       ((equal "SPLINE" (cdr (assoc 0 enx)))        
   (setq splin (1+ splin))
   (entdel ent)
        )
       );; cond
   );; If    
       );; Repeat
   
       ;(princ "\nNo Small stuff found.")
   )
(if (setq ss (ssget "x" '((0 . "POINT"))))
  (progn
    (setq pont (sslength ss))
    (command "_.erase" ss "")
   )
  
  )  
(princ (strcat (itoa arcs) " Arcs, " (itoa lin) " Lines, " (itoa circ) " Circles, " (itoa plin) " Polylines, " (itoa pont) " Points, and " (itoa splin) " Splines " "Deleted"))
   (princ)
)
(vl-load-com)

 

A good effort, well done.

 

Since you are keen to have a detailed output, rather than collecting the number of objects of each entity type using a separate variable, I would suggest building an association list to store the quantities of each type such that the code does not need to be modified to accommodate additional entity types:

(defun c:delsmbits ( / ent enx idx itm lst sel tol typ )
   (initget 6)
   (setq tol (cond ((getdist "\nSpecify minimum object length <0.5>: ")) ( 0.5 )))

   (if (setq sel (ssget "_A" '((0 . "ARC,CIRCLE,LINE,LWPOLYLINE,POINT,SPLINE"))))
       (repeat (setq idx (sslength sel))
           (setq ent (ssname sel (setq idx (1- idx)))
                 enx (entget ent)
                 typ (cdr (assoc 0 enx))
           )
           (if (or (= "POINT" typ) (<= (vlax-curve-getdistatparam ent (vlax-curve-getendparam ent)) tol))
               (progn
                   (entdel ent)
                   (if (setq itm (cdr  (assoc typ lst)))
                       (setq lst (subst (cons typ (1+ (cdr itm))) itm lst))
                       (setq lst (cons  (cons typ 1) lst))
                   )
               )
           )
       )
   )
   (princ "\n")
   (foreach itm lst
       (princ (strcat (itoa (cdr itm)) " " (strcase (car itm) t) "s, "))
   )
   (princ (if lst "deleted." "No objects deleted."))
   (princ)
)
(vl-load-com)

Here is a detailed explanation of how this works: Building an Association List

Link to comment
Share on other sites

As for the 'how it works', have you first read the documentation for each of the curve functions used?

http://help.autodesk.com/view/ACD/20...3-C886E528350A

 

No, I couldn't find anything with a Google & I read somewhere it's documented only in Russian. I shall have a good read of the link.

 

Firstly, a little-known fact is that the vlax-curve-* functions will accept entity name arguments (and will actually perform faster with enames over vla-objects), therefore, the expression to calculate the length can become:

(vlax-curve-getdistatparam ent (vlax-curve-getendparam ent))

Much better & no (cdr(car needed!

As for your code Lee, I'd like to study it thoroughly to learn how it all works before I undoubtedly use it.

 

Thanks Lee, you're extremely helpful

Link to comment
Share on other sites

No, I couldn't find anything with a Google & I read somewhere it's documented only in Russian. I shall have a good read of the link.

 

Only the ActiveX (COM) component of Visual LISP has been historically poory documented (though, there is now better documentation to be found here - note that the ActiveX documentation has always been written for VBA, however the translation from VBA to VL is only a change of syntax); the vlax- & vl- functions have always been documented - see here [EDIT: that link doesn't seem to work on the Autodesk site - try this instead and click on 'V' on the alphabetical list]

 

Much better & no (cdr(car needed!

As for your code Lee, I'd like to study it thoroughly to learn how it all works before I undoubtedly use it.

 

Thanks Lee, you're extremely helpful

 

Thank you Harry - let me know if you have further questions.

 

Lee

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