Rebuild Multiple Splines in AutoCAD - autocad

I am looking for some function that will allow me to automatically "rebuild" 1 or more splines in AutoCAD. I have drawings that have hundreds of splines with 30-50 control vertices each. This makes drawings very slow to work with, especially when interacting directly with a group of these splines.
I have the basic code for what I want to do, but am not sure at this point how to use the cvrebuild command in AutoLISP. Using that command in the command line simply brings up a GUI. See the code below for what I have so far.
I simply want to invoke the cvrebuild command using the variables n_controlvertices and degree as arguments. The AutoLISP routine would go through one object at a time and rebuild them with the same parameters.
I apologize for the appearance of the code. Apparently AutoLISP does not play well with StackOverflow
;; Batch rebuild splines
;;defines command name and variables
(defun c:batchrebuild (/ ss n obj n_controlvertices degree)
;; asks for selection
(prompt
"\nSelect splines to be rebuilt ."
)
;;decides if any splines are selected, and if not selects all
(if (not (setq ss (ssget '((0 . "SPLINE")))))
(setq ss (ssget "_X" '((0 . "SPLINE"))))
)
;;sets allowable entry to [2 (only nonzero) + 4 (only positive)]
(initget 6)
;;asks for number of fit points. if nothing is entered, it gives it the default value of 20
(setq n_controlvertices (getint "\nNumber of control vertices<20>: "))
(if
(= n_controlvertices nil)
(setq n_controlvertices 20)
(setq n_controlvertices (fix n_controlvertices))
)
;;asks for degree of fit points. if nothing is entered, it gives it the default value of 3
(setq degree (getint "\nDegree of fit points<3>: "))
(if
(= degree nil)
(setq degree 3)
(setq degree (fix degree))
)
(repeat (setq n (sslength ss))
(setq obj (vlax-ename->vla-object (ssname ss (setq n (1- n))))
;;(command cvrebuild)
;;This is the part that I am not sure about
)
(princ)
)

Here's a way.
It calls the command line version of CVREBUILD (-CVREBUILD).
It deals with system variables for the user inputs settings.
;; Batch rebuild splines
;;defines command name and variables
(defun c:batchrebuild (/ ss n obj n_controlvertices degree rebuild2doption rebuild2ddegree rebuild2dcv cmdecho)
;; asks for selection
(prompt "\nSelect splines to be rebuilt.")
;;decides if any splines are selected, and if not selects all
(or (setq ss (ssget '((0 . "SPLINE"))))
(setq ss (ssget "_X" '((0 . "SPLINE"))))
)
;; checks if the selection is not empty
(if ss
(progn
;;sets allowable entry to [2 (only nonzero) + 4 (only positive)
(initget 6)
;;asks for number of fit points. if nothing is entered, it gives it the default value of 20
(setq n_controlvertices
(cond
((getint "\nNumber of control vertices<20>: "))
(T 20)
)
)
;;asks for degree of fit points. if nothing is entered, it gives it the default value of 3
(setq degree (cond
((getint "\nDegree of fit points<3>: "))
(T 3)
)
)
;; saves the sysvars current values
(setq rebuild2doption (getvar "REBUILD2DOPTION")
rebuild2ddegree (getvar "REBUILD2DDEGREE")
rebuild2dcv (getvar "REBUILD2DCV")
cmdecho (getvar "CMDECHO")
)
;; sets the sysvars values according to user inputs
(setvar "REBUILD2DOPTION" 1)
(setvar "REBUILD2DDEGREE" degree)
(setvar "REBUILD2DCV" n_controlvertices)
(setvar "CMDECHO" 0)
;; rebuilds the selected splines
(repeat (setq n (sslength ss))
(command "_-cvrebuild" (ssname ss (setq n (1- n))))
)
;; restores sysvars initial values
(setvar "REBUILD2DOPTION" rebuild2doption)
(setvar "REBUILD2DDEGREE" rebuild2ddegree)
(setvar "REBUILD2DCV" rebuild2dcv)
(setvar "CMDECHO" cmdecho)
)
)
(princ)
)

Related

My code works, although only on my machine

I'm creating some AutoLisp commands for my team, and now that I'm finished, the code breaks apart in their computer and I can't figure out why. Works fine in mine.
The idea of the code is to stretch a polyline and update the block attributes that is grouped with.
The code asks to select the block, the actual width of the polyline and the fraction that is is supposed to take (ex: 0.75 to reduce to 75%).
Then, and here is were the problem starts, select the sides to stretch. On their computers, he does not allow to select, it simply jumps ahead.
(defun c:MRV (/ a b c d e)
;ungroup
(command "pickstyle" 0)
;variables
(setq blk (entsel "\nSelect block to modify: "))
(initget (+ 1 2 4))
(setq a (getreal "\nWidth?"))
(initget (+ 1 2 4))
(setq b (getreal "\nNew module fraction? (>0;1<)"))
;distance to reduce
(setq c (- 1 b))
(setq d (* a c -0.5))
(setq e (* -1 d))
;stretch
(command "stretch" pause pause "" "0,0" (polar '(0 0) (/ pi 2) d))
(command "stretch" pause pause "" "0,0" (polar '(0 0) (/ pi 2) e))
;open layer
(setq LayerTable (vla-get-layers (vla-get-activedocument (vlax-get-acad-object))))
(if (and (tblsearch "LAYER" "MC_BLOCO_INFO_AREAS")
(setq layname (vla-item layertable "MC_BLOCO_INFO_AREAS"))
(= (vlax-get-property layname 'lock) :vlax-true)
)
(vla-put-lock layname :vlax-false))
;change attribute
(setq l (cons "CAMPO_6" (rtos b 2 2)))
(LM:SetAttributeValues (car blk) (list l))
;close layer
(setq LayerTable (vla-get-layers (vla-get-activedocument (vlax-get-acad-object))))
(if (and (tblsearch "LAYER" "MC_BLOCO_INFO_AREAS")
(setq layname (vla-item layertable "MC_BLOCO_INFO_AREAS"))
(= (vlax-get-property layname 'lock) :vlax-false)
)
(vla-put-lock layname :vlax-true))
;update block width
(command "regenall")
;regroup
(command "pickstyle" 1)
(print "Modulo modificado.")
(princ)
)
(defun LM:SetAttributeValues ( blk lst / enx itm )
(if (= "ATTRIB" (cdr (assoc 0 (setq enx (entget (setq blk (entnext blk)))))))
(if (setq itm (assoc (strcase (cdr (assoc 2 enx))) lst))
(progn
(if (entmod (subst (cons 1 (cdr itm)) (assoc 1 enx) enx))
(entupd blk)
)
(LM:SetAttributeValues blk lst)
)
(LM:SetAttributeValues blk lst)
)
)
)
What should be happening:
When the AutoCAD STRETCH command issues the prompt for a selection of objects selected using a crossing window (crossing the segments that are to be stretched), the prompt is a standard selection prompt and the STRETCH command will subsequently obtain information about how the selection was acquired in the same way as you might using the AutoLISP ssnamex function.
As such, I would suggest supplying the STRETCH command with a selection set which has already been acquired using a crossing window selection method.
For example, you might define a function such as:
(defun mystretch ( dis / pt1 pt2 sel )
(while
(and
(setq pt1 (getpoint "\nSpecify first point of crossing window: "))
(setq pt2 (getcorner pt1 "\nSpecify opposite point of crossing window: "))
(not (setq sel (ssget "_C" pt1 pt2)))
)
(princ "\nNo objects were found within the crossing window.")
)
(if sel
(progn
(command "_.stretch" sel "" "_non" '(0 0) "_non" (list 0 dis))
t
)
)
)
You can then evaluate the above function with the distance that you wish to stretch the objects in the Y-direction, e.g.:
(mystretch 10.0)
Or, using the variables in your code:
(mystretch d)
(mystretch e)
The function will then return t (True) if the user has supplied two valid points and the STRETCH command has been issued - you can test for this in your program before proceeding.
Using this approach you can ensure that the user has supplied two points defining a crossing window which intersects one or more objects prior to issuing the AutoCAD STRETCH command.
The use of the ssget crossing mode string (C) also ensures that you are always supplying the STRETCH command which has been obtained using a crossing selection method.
You may also wish to refer to this answer regarding the use of the _non object snap modifier and also the _. command prefix in the above example code.

Execute Code On Lisp Exit

I have a lisp written that involves setting a variable, then selecting points inside of a loop. Once I decide that I am done selecting points, I would like to be able to revert that variable back to what it was originally when I press the escape key.
eg.
(defun c:df ()
(setq oom (getvar "osmode")) ;store current state
(setq type(getint "\nEnter Type: 1 For Horizontal, 2 For Vertical : "))
(setq startpt (getpoint "\nChoose Start Point : "))
(setq ptx (+ (nth 0 startpt)10))
(setq pty (+ (nth 1 startpt)10))
(setvar "osmode" 2); change state state
(while
(setq nextpt (getpoint "'Pick Mid: ")) ;make selection
(if (null nextpt) ((princ "\nNull Value Error.") return))
(if (= type 1) (command "dimlinear" startpt nextpt "H" (list 0 pty) ))
(if (= type 2) (command "dimlinear" startpt nextpt "V" ptx ))
(setq ptx (+ 5 ptx))
(setq pty (+ 5 pty))
)
;do after escape key is pressed.
(setvar "osmode" oom) ;revert state back to original.
)
I have found possible leads to do with "User Input Errors" but couldn't really get anything to work. To my understating, when escape is pressed lisp just exits and doesn't finish executing.
Thanks in advance.
AutoLISP considers a cancellation an error.
You can therefore manage the cancellations with an error handling. AutoLISP provides the *error* function that can be locally redefined.
In addition, I would like to make a few recommendations:
do not use the type symbol for a variable, it is the name of a built-in AutoLISP function
declare the variables locally (imperatively the *error* function)
use getkword and initget to let the user choose an option.
(defun c:df (/ *error* oom option startpt ptx pty nextpt) ; local variables
;; *error* local redefinition
(defun *error* (msg)
(if (/= msg "Function cancelled")
(princ (strcat "\nError: " msg))
)
(if oom
(setvar "osmode" oom)
)
(princ)
)
(setq oom (getvar "osmode")) ;store current state
(initget 1 "Horizontal Vertical")
(setq option (getkword "\nChoose an option [Horizontal/Vertical]: "))
(if (setq startpt (getpoint "\nChoose Start Point : "))
(progn
(setq ptx (+ (car startpt) 10))
(setq pty (+ (cadr startpt) 10))
(setvar "osmode" 2) ; change state state
(while (setq nextpt (getpoint "'Pick Mid: ")) ;make selection
(if (= option "Horizontal")
(command "_dimlinear" startpt nextpt "H" (list 0 pty))
(command "_dimlinear" startpt nextpt "V" (list ptx 0))
)
(setq ptx (+ 5 ptx))
(setq pty (+ 5 pty))
)
(setvar "osmode" oom) ;revert state back to original.
)
)
(princ)
)

Text to Mtext within Area - autocad

I have the following code. It creates text to mtext without moving the text blocks in autocad. I want to have this script but combine the text lines into one block within a certain area. As in create a block of mtext within 5 units north and south of a certain layer's text blocks.
(defun C:T1MJ ; = Text or Attribute Definition to 1-line Mtext, retaining Justification
(/ *error* cmde doc tss inc tent tobj tins tjust)
(defun *error* (errmsg)
(if (not (wcmatch errmsg "Function cancelled,quit / exit abort,console break"))
(princ (strcat "\nError: " errmsg))
); if
(vla-endundomark doc)
(setvar 'cmdecho cmde)
(princ)
); defun - *error*
(setq
cmde (getvar 'cmdecho)
doc (vla-get-activedocument (vlax-get-acad-object))
); setq
(vla-startundomark doc)
(setvar 'cmdecho 0)
(prompt "\nTo change Text/Attribute to 1-line Mtext, preserving Justification,")
(if (setq tss (ssget "_:L" '((0 . "TEXT,ATTDEF"))))
(repeat (setq inc (sslength tss))
(setq
tent (ssname tss (setq inc (1- inc)))
tobj (vlax-ename->vla-object tent)
tins (vlax-get tobj 'TextAlignmentPoint)
tjust (vla-get-Alignment tobj)
); setq
(cond
((= tjust 0) (setq tjust 7 tins (vlax-get tobj 'InsertionPoint))); Left
((< tjust 3) (setq tjust (+ tjust 7))); 1/2 [Center/Right] to 8/9
((= tjust 4) (setq tjust 5)); Middle to Middle-Center
((member tjust '(3 5)); Aligned/Fit
(setq
tjust 8 ; to Bottom-Center
tins (mapcar '/ (mapcar '+ (vlax-get tobj 'InsertionPoint) tins) '(2 2 2))
; with new insertion point
); setq
); Aligned/Fit
((setq tjust (- tjust 5))); all vertical-horizontal pair justifications
); cond
(if (= (vla-get-TextString tobj) "") (vla-put-TextString tobj (vla-get-TagString tobj)))
;; if no default content, disappears after TXT2MTXT: impose Tag value for it
;; [to use Prompt value instead, change end to (vla-get-PromptString tobj).]
(command "_.txt2mtxt" tent ""); convert, then
(setq tobj (vlax-ename->vla-object (entlast))); replace Text as object with new Mtext
(vla-put-AttachmentPoint tobj tjust); original Text's justification [or equiv.]
(vlax-put tobj 'InsertionPoint tins); original Text's insertion
); repeat
); if
(setvar 'cmdecho cmde)
(vla-endundomark doc)
(princ)
); defun -- T1MJ
(vl-load-com)
(prompt "\nType T1MJ to change Text/Attribute-Definitions to 1-line Mtext, preserving Justification.")
I am not a lisp programmer so I can't give you a direct answer but I am about to describe a concept to you that you should be able to replicate.
If you look here at this web resource it discusses one of the Express Tools: TXT2MTXT
Now, this is a command line routine and it takes a selection set and converts the TEXT into a MTEXT object:
So, I can't see why you can't use lisp to make a localized selection set of your text objects and then pass this selection set to the TXT2MTXT command. I know it is possible to do this kind of thing with lisp. I just don't know the mechanics. I know VBA.
I hope this is of assistance to you in resolve your issue. It doesn't show the code but it describes the concept of how to do what you want.

autolisp list used as points in Autocad

I'm new to LISP in autocad. The code shown below draws circles (with radius of 1) in a sloped line. What I don't understand is the value of "a" does not increase in increments of 1. The center of the circle drawn in autocad is (1,1) , (1.7071,1.7071) , (3,3) , (3.7071,3.7071) , (5,5) ... Can someone pls. explain why?
(defun c:wwq ()
(setq a 0)
(while (< a 10)
(setq a (+ 1 a))
(setq pt1 (list a a ) )
(command "circle" pt1 1 )
)
)
While using the AutoLISP command function, you have to care about active object snaps.
One way is to force object snaps to "none" within the (command ...) expression:
(defun c:wwq (/ a pt1)
(setq a 0)
(while (< a 10)
(setq a (+ 1 a))
(setq pt1 (list a a))
(command "_circle" "_none" pt1 1)
)
(princ)
)
Or, you can deactivate every osnap by setting the OSMODE system variable to 0 at the begining of the code and retore the previous value at the end (to be really safe, this method should need and error handler to insure the the previous value is reset in case an error occur during the code execution).
(defun c:wwq (/ a pt1 os)
(setq a 0
os (getvar 'osmode)
)
(setvar 'osmode 0)
(while (< a 10)
(setq a (+ 1 a))
(setq pt1 (list a a))
(command "_circle" pt1 1)
)
(setvar 'osmode os)
(princ)
)
Another way is to use the entmake function which is faster and do not care about osnaps.
(defun c:wwq (/ a)
(setq a 0.0)
(while (< a 10.0)
(setq a (+ 1.0 a))
(entmake
(list
(cons 0 "CIRCLE")
(list 10 a a 0.0)
(cons 40 1.0)
)
)
)
(princ)
)

How to select closed 2d polylines that don't have a hatch inside?

I have the following 2D polylines in autocad.
I'm trying to create a code that when selecting all of them, it will filter out those who have a hatch inside.
From another source I've got the following piece of code (thanks tharwat), but, altough I understand every piece of it, from the second ssget I can't understand what those elements mean together.
(defun c:test (/ ss i sn e)
(if (setq ss (ssget '((0 . "POLYLINE")))) ;;selects all the polylines in a window
(repeat (setq i (sslength ss)) ;;cycles trough each one of them
(if (ssget "_CP" ;;???defines a crossing poligon inside wich the polylines will be considered???
(mapcar 'cdr ;;???
(vl-remove-if-not '(lambda (p) (= (car p) 10))
(entget (setq sn (ssname ss (setq i (1- i)))))
)
)
'((0 . "HATCH"))
)
(ssdel sn ss) ;;deletes the entities wich belong to the selection set
)
)
)
(sssetfirst nil ss)
(princ)
)
Beginner here, sorry if this is not a good question.
"_CP" effectively stands for Crossing Polygon.
This option requires a list of points (the polygon vertices).
(mapcar 'cdr ;;???
(vl-remove-if-not '(lambda (p) (= (car p) 10))
(entget (setq sn (ssname ss (setq i (1- i)))))
)
)
Builds this list of points from the polyline vertices.
So, this routine first prompt for the user to select polylines.
Then, iterates through the selection set and, for each selected polyline, tries to select any hatch by crossing polygon with the polyline vertices. If any the polyline is removed from the first selection set.
In my opinion, as is, this code isn't really safe for your goal due to the 'Crossing' option. Replacing "_CP" with "_WP" will use Window Polygon selection which is safer if polylines do not have arc segments.

Resources