Removing Surfaces and Regions Above Set Z-Axis

Removing Surfaces and Regions Above Set Z-Axis

Anonymous
Not applicable
906 Views
12 Replies
Message 1 of 13

Removing Surfaces and Regions Above Set Z-Axis

Anonymous
Not applicable

I am currently working on writing a Autolisp script that will allow me to flatten a specific region of a 3D solid after removing all other regions and surfaces. I have no prior experience with autolisp so my code has been a product of what I can work out from research. The program starts with a copy and move of the 3D solid, so I can overlay the solid on the flattened profile after running all the commands on the solid that is to be flattened. This beginning code looks like this:

 

(defun C:SE1 ()
  (setq select1 (ssget "W" '(0 0) '(150 100)))
  (command "copy" select1 "" "0,0,0" "150,0,0")
  (command "explode" select1 "")
  (princ)
)

 I think I will be able to work out the code required to flatten and join the polyline (this will be a seperate fuction as the user needs to remove lines manually before the flatten).

 

If you look at the file I have attached and explode the solid you can see the number of regions/surfaces it creates. Along the lower edge, there is a surface that runs the full outer perimeter. My goal is to remove everything but just that surface. So far I have tried to find some ssget funtion that will allow me to select everything above that Z-axis, but there doesn't seem to be a easy way to approach it. It seems that using a filter value would make sense:

 

(setq selectdel (ssget "X" '((-4 . "*,*,<>")(100 0.0 0.0 1.0))))
(command "erase" selectdel "")

 The issue is that I don't think -4 is the correct code, but I can't find out what would be the right group code. The second concept I tried was using a rotation of the ucs to make it a operation of the xy-axis instead of the z-axis:

 

(command "ucs" "x" "-90")
(setq selectdel (ssget "w" '(0.1 0.1) '(-100.0 -100.0)))
(command "erase" selectdel "")

 This doesn't seem to work either, possibly because it doesnt recognize all the things with non-zero z-axis values?

 

It is very likely my mindset for the code is not correct for lisp, as I am basically trying to get it to replicate the steps that I can preform manually. If you can help point me in the correct direction then it would be much appriciated.

0 Likes
Accepted solutions (1)
907 Views
12 Replies
Replies (12)
Message 2 of 13

stevor
Collaborator
Collaborator

Another way of getting the entity names of the parts of the subject

is the record the last entity name in the data base

just before your copy or explode steps, with the  (entlast) function.

Then, after the explode,  the entnext from it until the end of the data base,

to get all the part from the explode results.

Search for examples, if they do not get posted.

S
0 Likes
Message 3 of 13

Anonymous
Not applicable

Thank you for the response stevor. I am a bit confused with your statement though. Even with the ability to cycle through the different parts in the exploded solid, I still need to be able to reliably not select the bottom region in a automated way. In particular when I use the same macro on a different part towards the same end, which will likely have a different order to the exploded list.

0 Likes
Message 4 of 13

Anonymous
Not applicable
First of all welcome to the community. Second, lisp works in list format. So it organizes all the data (objects, functions, ect) in a list. That is what stevor means. Using entlast automatically selects the last object in the list that the user selected. I recommend checking out lemac's page (http://www.lee-mac.com/) for helpful tips or I learned by doing the projects on Arfalisp (http://www.afralisp.net/index.php) good luck.
0 Likes
Message 5 of 13

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

.... 

(setq selectdel (ssget "X" '((-4 . "*,*,<>")(100 0.0 0.0 1.0))))
(command "erase" selectdel "")

.... The second concept I tried was using a rotation of the ucs to make it a operation of the xy-axis instead of the z-axis:

 

(command "ucs" "x" "-90")
(setq selectdel (ssget "w" '(0.1 0.1) '(-100.0 -100.0)))
(command "erase" selectdel "")

 This doesn't seem to work either, possibly because it doesnt recognize all the things with non-zero z-axis values?

....


One problem I see:
(setq selectdel (ssget "X" '((-4 . "*,*,<>") (100 0.0 0.0 1.0))))

is not going to work, partly because the (100) entries in entity data are entity-type designators such as (100 . "AcDbEntity") and [for Regions] (100 . "AcDbModelerGeometry"), not numerical values.  If that 100 was meant to be 10 instead, that might find all Lines and Polylines whose start points do not have a Z coordinate of 1.0, and Circles and Arcs whose centers do not, and Blocks and Xrefs whose insertion points do not.  But Regions have no (10) entry in their entity data, which is mysterious and inscrutable in its geometrical aspects, so I don't think you can filter for positional information about them in (ssget).

 

But your second concept might work if you rotate the UCS, Select everything [if done right after Exploding, the pieces will be simply the Previous selection] and Remove from the selection a Window that would include the base plane level of the original Solid and extend downward from there to miss all the stuff that's above that level, then Erase the resulting selection.  For the Window aspect to work, I suspect you might need to actually be looking at things from the rotated UCS's point of view, that is, either have the UCSFOLLOW System Variable set to 1, or run the PLAN command before the selection.

 

By the way, there's a peculiarity about Explode in a (command) function that means the "" you have at the end is not needed -- in fact, it needs not to be there.  The giving of the selection set to the Explode command will end the command [and Explode only one thing in the set if it contains more than one], so the extra Enter will try to recall the previous command entered outside a (command) function, which will usually give you various kinds of unwanted results.

Kent Cooper, AIA
0 Likes
Message 6 of 13

hmsilva
Mentor
Mentor
Accepted solution

Hi RyanRooker,

 

perhaps something like this:

 

(defun C:SE1 (/ i llpt mn obj select1 selectdel)
  (setq select1 (ssget "W" '(0 0) '(150 100)))
  (command "copy" select1 "" "0,0,0" "150,0,0")
  (command "explode" select1)
  (setq selectdel (ssget "P"))
  (repeat (setq i (sslength selectdel))
    (setq obj (vlax-ename->vla-object (ssname selectdel (setq i (1- i)))))
    (vla-getboundingbox obj 'mn 'mx)
    (setq llpt (vlax-safearray->list mn))
    (if (minusp (caddr llpt))
      (vla-delete obj)
    )
  )
  (princ)
)

 

Hope this helps,
Henrique

EESignature

0 Likes
Message 7 of 13

stevor
Collaborator
Collaborator

HMSilva's method does the same thing.

S
0 Likes
Message 8 of 13

Kent1Cooper
Consultant
Consultant

RyanR@Anonymous wrote:
...Along the lower edge, there is a surface that runs the full outer perimeter. My goal is to remove everything but just that surface.
@hmsilva wrote:

....

 

....
    (if (minusp (caddr llpt))
      (vla-delete obj)
    )
....

....


That appears intended to remove anything of the results of Exploding the Solid that extends below the 0 level in the Z direction.  The bottom surface Region from the original Solid in the sample drawing is at an elevation of -1.25, and therefore it would be deleted, but the request is to keep it.  Presumably the elevation of the bottom surface could be anywhere -- a Z coordinate of 0 would not always be relevant.

 

I would think what is wanted is to get the bounding box of the Solid before Exploding it, and delete anything from the results whose upper bound reaches above that level.  If a let's-call-it 'bott' variable holds the Z coordinate of the bottom of the original Solid's bounding box, and the individual object's upper right bounding box corner is saved [here, as 'urpt'] instead of its lower left, then something like:

....

    (if (> (caddr urpt) bott)

      (vla-delete obj)

    )

....

Kent Cooper, AIA
0 Likes
Message 9 of 13

hmsilva
Mentor
Mentor

@Kent1Cooper wrote:

RyanR@Anonymous wrote:
...Along the lower edge, there is a surface that runs the full outer perimeter. My goal is to remove everything but just that surface.


My bad!

I thought it was to delete everything below zero...

 

Henrique

 

EESignature

0 Likes
Message 10 of 13

hmsilva
Mentor
Mentor

Hi Ryan,

 

'...Along the lower edge, there is a surface that runs the full outer perimeter. My goal is to remove everything but just that surface.'

 

the following quick and dirty code, is just to show a possible way to erase everything but the surface that runs the full outer perimeter, it is not a completed code, to do so, you'll need to validate the entities returned from 'entlast' and selection sets, add some error handler, undo, etc.

 

(vl-load-com)
(defun c:demo (/ ENT ENT1 I LLPT LST MN OBJ POLY SS VLPOLY)

  ;; by me
  ;; Lwpolyline point list with fuzz in curve
  ;; obj - a vlaObject
  ;; fuzz - a real number
  (defun get_lst (obj fuzz / B D I LST PAR)
    (setq par 0)
    (while (< par (vlax-curve-getEndParam obj))
      (if (/= (vla-getbulge obj par) 0)
        (progn
          (setq d (- (vlax-curve-getDistAtParam obj (1+ par))
                     (setq b (vlax-curve-getDistAtParam obj par))
                  )
                i (1+ (fix (/ d fuzz)))
                d (/ d i)
          )
          (repeat i
            (setq lst (cons (trans (vlax-curve-getPointAtDist obj b) 0 1) lst)
                  b   (+ b d)
            )
          )
          (setq par (1+ par))
        )
        (setq lst (cons (trans (vlax-curve-getPointAtParam obj par) 0 1) lst)
              par (1+ par)
        )
      )
    )
    lst
  )

  (setq ss (ssget "W" '(0 0) '(150 100)))
  (command "_.copy" ss "" "0,0,0" "150,0,0")
  (command "_.explode" ss)
  (setq ss (ssget "P"))
  (repeat (setq i (sslength ss))
    (setq ent (ssname ss (setq i (1- i)))
          obj (vlax-ename->vla-object ent)
    )
    (vla-getboundingbox obj 'mn 'mx)
    (setq llpt (vlax-safearray->list mn))
    (if (not (minusp (caddr llpt)))
      (setq ent1 ent)
    )
  )
  (command "_.zoom" "_O" ent1 "")
  (command "_.explode" ent1)
  (setvar 'PEDITACCEPT 1)
  (command "_.pedit" "_M" "_P" "" "" "_J" "" "")
  (setq poly (entlast))
  (setq vlpoly (vlax-ename->vla-object poly))
  (vla-offset vlpoly 0.2)
  (command "_.erase" poly "")
  (setq poly (entlast))
  (setq vlpoly (vlax-ename->vla-object poly))
  (setq lst (get_lst vlpoly 0.2))
  (setq ss (ssget "CP" lst))
  (command "_.erase" ss poly "")
  (princ)
)

 

Hope this helps,
Henrique

EESignature

0 Likes
Message 11 of 13

Kent1Cooper
Consultant
Consultant

@Kent1Cooper wrote:
.....

I would think what is wanted is to get the bounding box of the Solid before Exploding it, and delete anything from the results whose upper bound reaches above that level.  If a let's-call-it 'bott' variable holds the Z coordinate of the bottom of the original Solid's bounding box, and the individual object's upper right bounding box corner is saved [here, as 'urpt'] instead of its lower left, then something like:

....

    (if (> (caddr urpt) bott)

      (vla-delete obj)

    )

....


Here's a variant on hmsilva's first suggestion, incorporating that approach.  [It does a couple of things a little differently, too, but you don't have to: asks for User selection instead of using a window, to account for a Solid at any location; copies the Solid in place so it's pre-overlaid on the flattened profile.]  Minimally tested, and without most of the usual controls, but it worked on your sample drawing.

 

(defun C:SBP (/ sol mn mx del bott i); = Solid's Base Plane
  (if (setq esel (entsel "\nSelect Solid to isolate its base plane: "))
    (progn
      (setq sol (car esel))
      (vla-getboundingbox (vlax-ename->vla-object sol) 'mn 'mx)
      (command
        "_.copy" sol "" "0,0,0" "" ; in place
        "_.explode" (entlast)
      ); command
      (setq
        del (ssget "_P"); pieces from Exploding
        bott (caddr (vlax-safearray->list mn)); "floor" level of Solid
      ); setq
      (repeat (setq i (sslength del))
        (setq obj (vlax-ename->vla-object (ssname del (setq i (1- i)))))
        (vla-getboundingbox obj 'mn 'mx)
        (if (> (caddr (vlax-safearray->list mx)) bott); extends higher than "floor" level?
          (vla-delete obj)
        ); if
      ); repeat
    ); progn
  ); if
  (princ)
); defun

Kent Cooper, AIA
0 Likes
Message 12 of 13

Anonymous
Not applicable

Thanks for all the help everyone, the advice provided has given me a good sense of how to approach the issue. I ended up using hmsliva's code and it worked great for the file I provided. I did need to spend a little while to figure out how exactly the code functions, but I think I have a better grasp on it now (though the VLA object convert still throws me a bit for the loop). As a way to gain a better understand of the code I have been trying to modify it to work for a solid that is projected upward instead of downward (file attached). Looking at the code it seems to be a matter of editing the following section:

 

    (setq llpt (vlax-safearray->list mn))
    (if (minusp (caddr llpt))
      (vla-delete obj)
    )

 My understanding of the code is that the llpt does not need to be modified as it functions to just cycle through all the entities in the list. This still needs to happen regardless of the orientation of the solid. My first concern was to use the plusp command instead of minusp, but that doesn't seem to exist in autolisp 2010. The other way would be to change the code to either:

 

    (setq llpt (vlax-safearray->list mn))
    (if not(zerop (caddr llpt))
      (vla-delete obj)
    )

 But this doesn't seem to function either. Am I misunderstanding the generation of llpt that would require me to edit the setq llpt command, or is my if statement not written correctly for autolisp formatting?

0 Likes
Message 13 of 13

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

.... I have been trying to modify it to work for a solid that is projected upward instead of downward (file attached). Looking at the code it seems to be a matter of editing the following section:

 

    ....
    (if (minusp (caddr llpt))
      (vla-delete obj)
    )

... the llpt does not need to be modified as it functions to just cycle through all the entities in the list. This still needs to happen regardless of the orientation of the solid. My first concern was to use the plusp command instead of minusp, but that doesn't seem to exist in autolisp 2010. The other way would be to change the code to either:

 

    ....
    (if not(zerop (caddr llpt))
      (vla-delete obj)
    )

 But this doesn't seem to function either. Am I misunderstanding the generation of llpt that would require me to edit the setq llpt command, or is my if statement not written correctly for autolisp formatting?


The conversion to a VLA object is needed in order to get the bounding box, and that returns "safearray" values, which then need to be converted into lists [point lists of XYZ coordinates] to be able to extract their Z coordinates.  So llpt is the lower left point of the object's bounding box, and (caddr llpt) is the Z coordinate of that, which is the elevation of its bottom-most point [or base plane, in the case of your example].

 

No, there's no (plusp) function.  If you use (not (zerop (caddr llpt))) [including the parenthesis for the (not) function that you're missing above], it will catch anything whose bottom level is not at 0, whether above or below.  But if it's extending upward from zero level, that's not what you want, because parts that sit on the zero level and extend upward will not be deleted.  You want to use the upper right corner of the bounding box, whose (caddr) value is the topmost level of the object, and check for things whose uppermost level is above 0:

 

(setq urpt (vlax-safearray->list mx))

(if (> (caddr urpt) 0) ;;;; EDIT -- fixed this line

  (vla-delete obj)

)

 

This all assumes that your thingies are always extending upward or downward from the WCS zero level, and that it's the Region remaining at the zero level that you want to keep.  If they might ever be at some other elevation, my variant on one of hmsilva's routines is designed to keep the Region at the bottom plane of the original 3D Solid, at whatever level that is, and could easily be adjusted to keep the top plane instead, or even to ask the User which they want to keep.

Kent Cooper, AIA
0 Likes