Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

sweep multple polylines at once. Code does not work *yet*

53 REPLIES 53
Reply
Message 1 of 54
l.l.oldescholtenhuis
6133 Views, 53 Replies

sweep multple polylines at once. Code does not work *yet*

Hi There,

 

I'm using AutoCAD Civil 3D to model inner city utility construction projects. For my research project, I need to include water and storm pipes, but also various sorts of cables. Currently, I'm manually creating cables profiles using plines from existing 2D drawings and the sweep function.. a very cumbersome taks. 

 

I was wondering whether there is a way to speed this process up. At the moment, I draw a circle, copy it myriads of times, and sweep each along the dozens of plines (one by one). I cannot select multiple lines and sweep them all, can I?

 

I posed a this question: "Can anybody tell me whether I can select mutiple plines to sweep all at once?" and was directed at the following code in the civil3D forum.

 

(defun c:sweepm ()
(princ "\PSelect objects to sweep: ")
(setq pick1 (ssget))
(princ "\nSelect sweep paths: ")
(setq ss (ssget '((0 . "LINE,*POLYLINE,3dpoly"))))
(setq NumPath (sslength ss) Count 0)
(repeat NumPath
(setq Ename (ssname ss Count))
(command "sweep" pick1 "" Ename)
(repeat NumPath
(setq Count (+ 1 count))
)
(princ)
)

 

 

I tried to implement it via VLISP. However, although the selection features allow me to select multiple objects and plines, autocad eventually only sweeps one line. Does anybody know why?

 

53 REPLIES 53
Message 21 of 54
hmsilva
in reply to: Kent1Cooper


@Kent1Cooper wrote:

...

 

EDIT:  Also, it looks like the routine does the Sweeping and then moves the result to the desired elevation.  I think the sequence should be different, if I'm interpreting Messages 8 and 10 correctly -- the Line/Polyline should be moved to the desired elevation first, then Sweep should be applied.

 

...

Kent,
my apologies, but when I answered I did not see the EDIT.

The order of operations has no influence on the final result, with exception, of the OP want to preserve the original lines, if so, you are completely correct, and will be necessary first change the elevation of the lines and then do the sweep...


Thank you!
Henrique

EESignature

Message 22 of 54

Hi Henrique and Kent,

 

Sorry for not responding so long, but I had to finish some other projects. I just played aroud with the script. It seems to do exactly what I want. As I'm not so much into LISP, i still have some questions you might be able to anwer:

 

- the script does not seem to work well with 3-point arcs. Do you know whether this might be resolved by using the sweep function instead of extrude?

 

- currently, all plines get the same diameter of tube. In reality, of course, electricity and water infrastructure are quite different in diameter. Would it be possible to add this nuance in the script? It might be nice if the drafter can insert diameters for each distinctive layer (e.g. water = 2, electricity = 0.5 etc.)

 

One additional challenge is the following: usually layer-names are not so nice as WATER, ELECTRICITY, GAS etc. I often get drawings with names such as water_replace, wtr, electr_highvolt, el, gas8Bar etc.  would it be possible for the TUBE-script to present these layers-names and ask the drafter to identify this as Water, Electricity or Gas category? 

 

 

Message 23 of 54

Léon,

 

"the script does not seem to work well with 3-point arcs. Do you know whether this might be resolved by using the sweep function instead of extrude?"

 

the code I post here, use the command "sweep"...

 

"It might be nice if the drafter can insert diameters for each distinctive layer (e.g. water = 2, electricity = 0.5 etc.)"

 

in the code I post, is prompted to insert the diameter...

 

If you want, only with the purpose of testing, changes to the correct layer names and the correct elevation the code I post, and try it.


Henrique

EESignature

Message 24 of 54

Hi Henrique,

 

It seems that the code asks for a diameter, but that sweeps all the plines with this diameter. Would it be possible to insert a diameter separately for each layer?

 

Best

Message 25 of 54


@l.l.oldescholtenhuis wrote:

.... 

- the script does not seem to work well with 3-point arcs. Do you know whether this might be resolved by using the sweep function instead of extrude?

 

- currently, all plines get the same diameter of tube. In reality, of course, electricity and water infrastructure are quite different in diameter. Would it be possible to add this nuance in the script? It might be nice if the drafter can insert diameters for each distinctive layer (e.g. water = 2, electricity = 0.5 etc.)

.... 


My earlier one works with Arcs for me.  In what way does it "not ... work well" for you?  [I can't test whether it's a matter of the difference between Sweep and Extrude.]

 

Here's a version that has separate diameters [but expressed as radii for simplicity in application] built in for each Layer [lightly tested]:

 

(defun C:TUBE (/ cmde osm elevrad ss ent lay)
  (setq
    cmde (getvar 'cmdecho)
    osm (getvar 'osmode)
    clay (getvar 'clayer)
    elevrad '("ELECTRICITY" 1 0.375 "WATER" -3 0.75 "GAS" -2.5 0.5)
      ; Layer names followed by elevation and conduit/pipe radius [edit numbers as appropriate]
  ); setq
  (setvar 'cmdecho 0)
  (setvar 'osmode 0)
  (prompt "\nTo put paths at Layer-based elevations and make tubular extrusions along them,")
  (if
    (setq ss (ssget '((0 . "*POLYLINE,LINE,ARC,CIRCLE,ELLIPSE,SPLINE") (8 . "ELECTRICITY,WATER,GAS"))))
    (repeat (sslength ss); then
      (setq
        ent (ssname ss 0)
        lay (strcase (cdr (assoc 8 (entget ent))))
      ); setq
      (command
        "_.move" ent ""
        (list 0 0 (cadr (member (setvar 'clayer lay) elevrad)))
          ; gets elevation value as item following Layer name in elevrad list,
          ; setting that Layer current in the process
        "" ; 'point' given was displacement -- finish Move
        "_.ucs" "_ZA"
          (vlax-curve-getStartPoint ent)
          (mapcar '+
            (vlax-curve-getStartPoint ent)
            (vlax-curve-getFirstDeriv ent (vlax-curve-getStartParam ent))
          ); mapcar & UCS
        "_.circle" "0,0" (caddr (member lay elevrad)); radius = 2nd number after Layer name
        "_.ucs" "_previous"
        "_.extrude" (entlast) "" "_path" ent
      ); command
      (ssdel (ssname ss 0) ss)
    ); repeat
    (prompt "\nNo qualifying object(s) selected."); else
  ); if
  (setvar 'cmdecho cmde)
  (setvar 'osmode osm)
  (setvar 'clayer clay)
  (princ)
); defun

 

Also, look into the DELOBJ System Variable, and set it appropriately for whether or not you would like to retain the Circles that it Extrudes.

 

EDIT:  It could be made to ask the User for diameters/radii each time [as the previous one did in the same-diameter-for-all approach], if that's preferable to building them into the code as constants.  Likewise, it could ask each time for the elevations to move each Layer's objects to.

Kent Cooper, AIA
Message 26 of 54


@l.l.oldescholtenhuis wrote:

....

One additional challenge is the following: usually layer-names are not so nice as WATER, ELECTRICITY, GAS etc. I often get drawings with names such as water_replace, wtr, electr_highvolt, el, gas8Bar etc.  would it be possible for the TUBE-script to present these layers-names and ask the drafter to identify this as Water, Electricity or Gas category? 


I imagine that could be done, but it sounds pretty wide open.  Would it be valid to assume that all Water-related Layer names would at least start with a W, all Electricity-related ones with an E, and all Gas-related ones with a G?  That would make it a lot easier, I think.

Kent Cooper, AIA
Message 27 of 54

I'm afraid that that even that is not the case. Oftentimes, the service providers have their own coding systems for these layers.
To circumvent this, I thought it would be nice to have a script that allows the user to select and allocate the various layers to categories Electricity, Water, Gas. Perhaps we can use dropdown-menus or checkboxes for this?

Subsequently, the script uses the settings for various categories to move and extrude the polylines on the various layer.

I don't know whether this is possible at all, but it seems to me the most convenient solution.
Message 28 of 54


@l.l.oldescholtenhuis wrote:
.... I thought it would be nice to have a script that allows the user to select and allocate the various layers to categories Electricity, Water, Gas. ....

You could do something like this [untested]:

 

(setq Electricity "" Water "" Gas ""); start with empty strings

(while

  (setq layname (cdr (assoc 2 (tblnext "layer" (not layname))))); name of next Layer

  (if

    (not

      (wcmatch

        (strcase layname)

        "0,DEFPOINTS,LAYER,NAMES,YOU,KNOW,ARE,NOT,IN,THOSE,CATEGORIES"

      ); wcmatch

    ); not

    (progn; then

      (initget 1 "Electricity Water Gas None")

      (setq cat

        (getkword

          (strcat "\nCategorize Layer " layname " [Electricity/Water/Gas/None]: ")

        ); getkword

      ); setq

      (if (/= cat "None"); User put into one of the categories

        (set (read cat) (strcat (eval (read cat)) layname ","); add to category's list of Layer names

      ); if [User-categorized]

    ); progn

  ); if [a Layer to be categorized or not]

); while [step through Layers]

 

Then you can use the variables Electricity, Water and Gas, which should hold comma-delimited strings of Layer names, in (ssget) filters to find the relevant things on all the Layers in each category together.

 

It could be built to have "None" be a default for which the User could hit Enter in categorizing, if you prefer.

Kent Cooper, AIA
Message 29 of 54
Kent1Cooper
in reply to: Kent1Cooper


@Kent1Cooper wrote:
....
     (set (read cat) (strcat (eval (read cat)) layname ","); add to category's list of Layer names

....


That line should have another right parenthesis at the end:

 

        (set (read cat) (strcat (eval (read cat)) layname ",")); add to category's list of Layer names

Kent Cooper, AIA
Message 30 of 54

I tried to integrate this with your previous script for extrusion. I think I did this wrong, as I cannot select any polyline for extrusion. Do you see where I need to adapt the complete script?

 

(defun C:TUBE (/ cmde osm elevrad ss ent lay)
(setq Electricity "" Water "" Gas ""); start with empty strings
(while
(setq layname (cdr (assoc 2 (tblnext "layer" (not layname))))); name of next Layer
(if
(not
(wcmatch
(strcase layname)
"0,DEFPOINTS,LAYER,NAMES,YOU,KNOW,ARE,NOT,IN,THOSE,CATEGORIES"
); wcmatch
); not
(progn; then
(initget 1 "Electricity Water Gas None")
(setq cat
(getkword
(strcat "\nCategorize Layer " layname " [Electricity/Water/Gas/None]: ")
); getkword
); setq
(if (/= cat "None"); User put into one of the categories
(set (read cat) (strcat (eval (read cat)) layname ",")); add to category's list of Layer names
); if [User-categorized]
); progn
); if [a Layer to be categorized or not]
); while [step through Layers]
elevrad '("ELECTRICITY" 1 0.375 "WATER" -3 0.75 "GAS" -2.5 0.5)
; Layer names followed by elevation and conduit/pipe radius [edit numbers as appropriate]
); setq
(setvar 'cmdecho 0)
(setvar 'osmode 0)
(prompt "\nTo put paths at Layer-based elevations and make tubular extrusions along them,")
(if
(setq ss (ssget '((0 . "*POLYLINE,LINE,ARC,CIRCLE,ELLIPSE,SPLINE") (8 . "ELECTRICITY,WATER,GAS"))))
(repeat (sslength ss); then
(setq
ent (ssname ss 0)
lay (strcase (cdr (assoc 8 (entget ent))))
); setq
(command
"_.move" ent ""
(list 0 0 (cadr (member (setvar 'clayer lay) elevrad)))
; gets elevation value as item following Layer name in elevrad list,
; setting that Layer current in the process
"" ; 'point' given was displacement -- finish Move
"_.ucs" "_ZA"
(vlax-curve-getStartPoint ent)
(mapcar '+
(vlax-curve-getStartPoint ent)
(vlax-curve-getFirstDeriv ent (vlax-curve-getStartParam ent))
); mapcar & UCS
"_.circle" "0,0" (caddr (member lay elevrad)); radius = 2nd number after Layer name
"_.ucs" "_previous"
"_.extrude" (entlast) "" "_path" ent
); command
(ssdel (ssname ss 0) ss)
); repeat
(prompt "\nNo qualifying object(s) selected."); else
); if
(setvar 'cmdecho cmde)
(setvar 'osmode osm)
(setvar 'clayer clay)
(princ)
); defun

Message 31 of 54


@l.l.oldescholtenhuis wrote:

I tried to integrate this with your previous script for extrusion. I think I did this wrong, as I cannot select any polyline for extrusion. Do you see where I need to adapt the complete script?

....

); while [step through Layers]
elevrad '("ELECTRICITY" 1 0.375 "WATER" -3 0.75 "GAS" -2.5 0.5)
....


I'll keep looking, but right off I notice a missing line.  The lines quoted above should have another line between them:

 

  ); while [step through Layers]

  (setq
    elevrad '("ELECTRICITY" 1 0.375 "WATER" -3 0.75 "GAS" -2.5 0.5)

 

EDIT:  And the (ssget) filter needs to be adjusted to look for all those categorized Layers, not just the ones [if they exist] explicitly named Electricity/Water/Gas.  I'll post an adjustment later, but I don't think I can work that out before I run out of time to edit this message.

Kent Cooper, AIA
Message 32 of 54


@l.l.oldescholtenhuis wrote:

I tried to integrate this with your previous script for extrusion. I think I did this wrong, as I cannot select any polyline for extrusion. Do you see where I need to adapt the complete script?

....


See comments [all preceded by ;;;;;] in the attached.  Minimally tested, but it seems to work.  It could still use error handling, Undo begin/end, etc.

Kent Cooper, AIA
Message 33 of 54
Kent1Cooper
in reply to: Kent1Cooper


@Kent1Cooper wrote:
....

See comments .... in the attached.  ....


It occurred to me that there might be situations in which an additional safeguard should be incorporated.  Suppose, for instance, you got a drawing, not very consistently constructed, in which there was a Layer called "OverheadWire" that you put in the "Electricity" category, and a Layer called simply "W" that you put in the "Water" category.  When the routine came to something on the "W" Layer, it would treat it as if it were in the "Electricity" category instead of the "Water" category, because it would find the "W" in "OverheadWire".  That kind of situation could be prevented by including the comma in the (wcmatch) function's pattern argument, since Layer names are put into the category strings with commas after them [even the last one in each category].  If that might be a potential problem, change this:

....

          (cond
            ((wcmatch Electricity (strcat "*" lay "*")) "Electricity")
            ((wcmatch Water (strcat "*" lay "*")) "Water")
            ((wcmatch Gas (strcat "*" lay "*")) "Gas")
          ); cond & cat

....

to this:

....

          (cond
            ((wcmatch Electricity (strcat "*" lay ",*")) "Electricity")
            ((wcmatch Water (strcat "*" lay ",*")) "Water")
            ((wcmatch Gas (strcat "*" lay ",*")) "Gas")
          ); cond & cat

....

 

There could be other remotely possible combinations of Layer names in which one is contained completely in another, that could conceivably result in the shorter one being included within a longer one in a different category, and if the overlap happened to be the end of the longer name [e.g., similarly to the example above, "OverheadW" and "W"], the fix above wouldn't protect you from things being processed incorrectly.  Such an incorrect result can also be prevented by starting all the category strings with a comma, i.e. change this line near the top:

....

  (setq Electricity "" Water "" Gas ""); start with empty strings

....

to this:

....

  (setq Electricity "," Water "," Gas ","); start strings with commas

....

and putting both the before-and-after commas in the (wcmatch) pattern arguments:

....

          (cond
            ((wcmatch Electricity (strcat "*," lay ",*")) "Electricity")
            ((wcmatch Water (strcat "*," lay ",*")) "Water")
            ((wcmatch Gas (strcat "*," lay ",*")) "Gas")
          ); cond & cat

....

That way, it will find only complete Layer names in their categories, and any partial overlap in Layer names won't be able to affect anything.

Kent Cooper, AIA
Message 34 of 54
danaag
in reply to: hmsilva

I found this from Henrique and it works great for me.

 

But I would like to have the options that reguler sweep has.
Select objects to sweep or [MOde]:
and
Select sweep path or [Alignment/Base point/Scale/Twist]:

Can anyone please help?

Message 35 of 54
hmsilva
in reply to: danaag

danaag,
can you explain more in detail what you intend to achieve with the commando sweep automation.

Henrique

EESignature

Message 36 of 54
Kent1Cooper
in reply to: danaag


@danaag wrote:

I found this from Henrique and it works great for me.

 

But I would like to have the options that reguler sweep has.
Select objects to sweep or [MOde]:
and
Select sweep path or [Alignment/Base point/Scale/Twist]:

Can anyone please help?


You don't indicate which of hmsilva's routines you're talking about, and I don't have the Sweep command to try it out, but I have a suggestion.  You should be able to replace this line [or the similar one in other routines]:

 

(command "sweep" cir "" hnd)

 

with something like:

 

(command "sweep")

(while (> (getvar 'cmdactive) 0) (command pause))

 

and it should hang around in the Sweep command while you do whatever you want to, before it goes on to the next thing only after that command is completed.  However, since the possibility of options means the selection parts won't always occur at the same point in the sequence of events, you would probably be stuck having to select the Circle and the path yourself for each Sweep, rather than having the routine feed them to the command.  That means you would probably need to alter the whole thing to process only one at a time, instead of a whole selection set at once.

Kent Cooper, AIA
Message 37 of 54

I realised I did not post the actual routine, here it is:

(defun c:test (/ cir cir1 hnd itm num ss ss1)
(prompt "\nSelect object to sweep: ")
(if (setq ss1 (ssget "_: S:E:L" '((0 . "CIRCLE"))))
(progn
(prompt "\nSelect sweep paths: ")
(if (setq ss (ssget '((0 . "LINE,*POLYLINE,3dpoly"))))
(progn
(setq cir (ssname ss1 0))
(setq itm 0
num (sslength ss)
)
(while (< itm num)
(setq hnd (ssname ss itm))
(vl-cmdf "_copy" cir "" "0,0,0" "0,0,0")
(setq cir1 (entlast))
(command "sweep" cir1 "" hnd)
(setq itm (1+ itm))
);; while
);; progn
);; if
);; progn
);; if
(princ)
);; test

Basically I just need the option to select base point, for example sweep a circle along several 3d polylines with bottom quadrant point as base point.

thank for helping
regards Dan
Message 38 of 54
hmsilva
in reply to: danaag

danaag,
I think that does what you want.
Try it, is minimally tested...

 

(defun c:test (/ cir cir1 hnd itm num ss ss1 pt)
  (prompt "\nSelect object to sweep: ")
  (if (and(setq ss1 (ssget "_:S:E:L" '((0 . "CIRCLE"))))
	(setq old_osm (getvar "osmode"))
	(setvar "osmode" 16)
	(setq pt (getpoint "\nSelect Quadrant Base Point ; " ))
	(setvar "osmode" old_osm))
    (progn
      (prompt "\nSelect sweep paths: ")
      (if (setq ss (ssget '((0 . "LINE,*POLYLINE,3dpoly"))))
	(progn
	  (setq cir (ssname ss1 0))
	  (setq	itm 0
		num (sslength ss)
	  )
	  (while (< itm num)
	    (setq hnd (ssname ss itm))
	    (vl-cmdf "_copy" cir "" "0,0,0" "0,0,0")
	    (setq cir1 (entlast))
	    (command "sweep" cir1 "" "base" pt hnd)
	    (setq itm (1+ itm))
	  );; while
	);; progn
      );; if
    );; progn
  );; if
  (princ)
);; test

 

Henrique

EESignature

Message 39 of 54
Kent1Cooper
in reply to: danaag


@danaag wrote:
....
(if (setq ss (ssget '((0 . "LINE,*POLYLINE,3dpoly"))))
....

[Not directly on the question, but I noticed the line above or a similar one in several posts here.  There is no "3dpoly" entity type as an (assoc 0) value in entity data, so the end of that string doesn't contribute anything to the filtering.  The *POLYLINE part will let it find 3DPolylines.]

Kent Cooper, AIA
Message 40 of 54
hmsilva
in reply to: Kent1Cooper


@Kent1Cooper wrote:

@danaag wrote:
....
(if (setq ss (ssget '((0 . "LINE,*POLYLINE,3dpoly"))))
....

[Not directly on the question, but I noticed the line above or a similar one in several posts here.  There is no "3dpoly" entity type as an (assoc 0) value in entity data, so the end of that string doesn't contribute anything to the filtering.  The *POLYLINE part will let it find 3DPolylines.]


My bad!!!
Had noticed this mistake and never fix it...

 

(defun c:test (/ cir cir1 hnd itm num ss ss1 pt)
  (prompt "\nSelect object to sweep: ")
  (if (and(setq ss1 (ssget "_:S:E:L" '((0 . "CIRCLE"))))
	(setq old_osm (getvar "osmode"))
	(setvar "osmode" 16)
	(setq pt (getpoint "\nSelect Quadrant Base Point ; " ))
	(setvar "osmode" old_osm))
    (progn
      (prompt "\nSelect sweep paths: ")
      (if (setq ss (ssget '((0 . "LINE,*POLYLINE"))))
	(progn
	  (setq cir (ssname ss1 0))
	  (setq	itm 0
		num (sslength ss)
	  )
	  (while (< itm num)
	    (setq hnd (ssname ss itm))
	    (vl-cmdf "_copy" cir "" "0,0,0" "0,0,0")
	    (setq cir1 (entlast))
	    (command "sweep" cir1 "" "base" pt hnd)
	    (setq itm (1+ itm))
	  );; while
	);; progn
      );; if
    );; progn
  );; if
  (princ)
);; test

Corrected...

 

Henrique

EESignature

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost