Jump to content

Recommended Posts

Posted

I need to write a script file to remove ALL layerstates in drawings.

 

Not all the layerstates are the same in the drawings tho any ideas?

 

(layerstate-delete "[color=red]______[/color]" nil)

Need to know what to put there for all other than naming each one. *.*?

 

Is there a way to purge all layer states?

 

 

Also how can I confirm that the layerstate restore options always have "apply properties as viewport" unchecked in a scrip file?

(layerstate-import "I:\\DRAWINGS\\090-0056 LFMP\\Common\\Layer_States\\ELLS.las")
(layerstate-restore "ELLS" nil)

  • Replies 26
  • Created
  • Last Reply

Top Posters In This Topic

  • Lee Mac

    9

  • irneb

    8

  • alanjt

    5

  • kam1967

    4

Top Posters In This Topic

Posted

Unfortunately the layerstate functions don't work with wildcards. So you need to find all the states inside the DWG. Unfortunately there's no already made function to do this. So here goes, a function to give you a list of states in the current drawing:

(defun layerstate-list (/ layer0 layerTable layerXDict stateDict state stateList)
 (if (and (setq layer0 (tblobjname "LAYER" "0")) ;Get the layer 0's ename
          (setq layer0 (entget layer0)) ;And its DXF codes
          (setq layerTable (cdr (assoc 330 layer0))) ;Get the ename of the layers table
          (setq layerTable (entget layerTable)) ;And its DXF codes
          (setq layerXDict (cdr (assoc 360 layerTable))) ;Get the layer table's XDictionary
          (setq stateDict (dictsearch layerXDict "ACAD_LAYERSTATES")) ;Get the layer states dictionary
     )
   (foreach state stateDict ;Repeat for all states
     (if (= (car state) 3) ;Check if it's a dictionary entry
       (setq stateList (cons (cdr state) stateList)) ;Add it to the list
     )
   )
 )
 stateList ;Return the generated list
)

Then all that needs to happen is step through them all:

(foreach state (layerstate-list) (layerstate-delete state))

  • 1 month later...
Posted

irneb - can you show me where this code is to be inserted in the above routine? I tried putting it in front of the closing parenthesis, but it's giving me an error. Thanks.

 

[/code]Then all that needs to happen is step through them all:

(foreach state (layerstate-list) (layerstate-delete state))

Posted

Hi Kam,

 

The snippet that Irneb provides at the end of the post is demonstrating how to call the main block of code in that post.

 

Hence, ensure the 'layerstate-list' code is loaded, then call something like:

 

(defun c:test ( )[font=monospace]
[/font]  (foreach state (layerstate-list) (layerstate-delete state))
 (princ)
)

Posted

Thanks, Lee. I thought about doing something like that, but then I thought it has to be within the function in order to use the layerstate-list variable. Guess I should've tested that first. If I recall, that "/" allows it to operate outside of the defun function, right?

 

Thank you both for your help! :)

Posted
but then I thought it has to be within the function in order to use the layerstate-list variable.

 

The 'layerstate-list' symbol is a function, not a variable. 'stateList' is the variable used within this function to hold the list, and this variable is returned by the function (since it is the last expression evaluated in the function).

 

If I recall, that "/" allows it to operate outside of the defun function, right?

 

No, the "/" declares those symbols following the "/" as local to the function, meaning those symbols exist in that function's 'namespace' (a container). This means the values held by those variables when used by the function cannot be accessed and won't interfere outside of the function.

 

Consider this example:

 

(setq test "Kam")
(print test)

(defun DoIt ( / test )
 (print test)
 (setq test "Lee")
 (print test)
 (princ)
)

(DoIt)
(print test)

When run, the above will return:

 

"Kam"   -   Value held by 'test' outside of function namespace
nil     -   Since 'test' is local to the function, the symbol has no value in the function namespace yet.
"Lee"   -   Value now held by 'test' symbol in the function namespace
"Kam"   -   Value held by 'test' outside of function namespace
           (not changed by the value of 'test' inside the function namespace)

Posted

Thanks for the explanation, Lee. I now understand the statelist variable. I wonder though, could this line be placed after the statelist variable or only from outside the function?

 

(foreach state (layerstate-list) (layerstate-delete state))

 

I understand that layerstate-list now contains the statelist information. If we modify the line to say below, I'm curious why it didn't it work within the function..? I tried it, but no go.

 

)

stateList ;Return the generated list

(foreach state (statelist) (layerstate-delete state))

)

Posted
I wonder though, could this line be placed after the statelist variable or only from outside the function?

 

(foreach state (layerstate-list) (layerstate-delete state))

 

No, otherwise we would be calling the 'layerstate-list' function recursively from within its own function definition.

 

I understand that layerstate-list now contains the statelist information.

 

It doesn't. 'layerstate-list' is the symbol pointing to the function definition, we call this function to return a list of layerstates.

 

If we modify the line to say below, I'm curious why it didn't it work within the function..? I tried it, but no go.

 

You could replace the 'stateList' return with:

 

(foreach state statelist (layerstate-delete state))
(princ)

 

To delete all the layerstates in the list held by the 'statelist' variable, with a 'princ' at the end to ensure a clean exit since we now don't need anything returned.

Posted

Hmm..I tried putting this in the end of the routine of the layerstate-list function, but it does not delete any layer states. It's alright. At least the other method works, Lee. Thank you, though.

 

)

; stateList ;Return the generated list

(foreach state stateList (layerstate-delete state))

(princ)

)

Posted
Hmm..I tried putting this in the end of the routine of the layerstate-list function, but it does not delete any layer states. It's alright. At least the other method works, Lee. Thank you, though.

 

)

; stateList ;Return the generated list

(foreach state stateList (layerstate-delete state))

(princ)

)

If you want to use the statelist variable, you have 2 options:

1) Modify the layerstate-list function and remove the declaration of the statelist variable in its header (i.e. you now make it a global variable instead of local to the defun).

2) Assign a new statelist variable through setq to the value returned by layerstate-list

 

In both cases you would still require a call to the defun in order for it to calculate the list. So if you go with option 1 you'd still need:

(layerstate-list)
(foreach state statelist .....

Option 2 would look something like:

(setq statelist (layerstate-list))
(foreach state statelist .....

However, I can't understand why you'd want to keep the list of state names after you've deleted them. Unless you got some other reason - maybe listing what has been deleted to a file, or re-creating them from some settings - though that could conceivably be done inside the foreach lop itself, thus no need to save the list into a variable. But if there is a need to have the list as a variable I'd go with option 2 every day and twice on Sundays! Try to steer clear of global variables as much as possible - they just cause problems when you least expect it. Not to mention it's clearer for others to understand what's going on when you use local vars - a global var is generally assigned its value somewhere else, only to be used at a later stage in a totally different part of (possibly) another LSP file: very prone to the human factor.

Posted

BTW, here's a more efficient (not to mention more "functional") version of the same code:

(vl-load-com)

;; Get a list of layerstate names from the current drawing, using a wildcard match
(defun layerstate-list (match / temp)
 (if (and (setq temp (tblobjname "LAYER" "0")) ;Get the layer 0's ename
          (setq temp (entget temp)) ;And its DXF codes
          (setq temp (cdr (assoc 330 temp))) ;Get the ename of the layers table
          (setq temp (entget temp)) ;And its DXF codes
          (setq temp (cdr (assoc 360 temp))) ;Get the layer table's XDictionary
          (setq temp (dictsearch temp "ACAD_LAYERSTATES")) ;Get the layer states dictionary
          (setq match (strcase match)) ;Ensure uppercase to allow for case-insensitivity
     )
   (vl-remove-if-not
     (function (lambda (item) (wcmatch (strcase item) match)))
     (mapcar 'cdr
             (vl-remove-if-not (function (lambda (item) (= (car item) 3))) temp)
     )
   )
 )
)

;; Delete layer states with wildcard match
(defun layerstate-delete-wcmatch (match /)
 (mapcar (function (lambda (item) (layerstate-delete item) item) (layerstate-list match)))
)

Note the new layerstate-list function doesn't even have any local variables except one temp which is reused constantly to obtain the layer 0 dxf data and it's dictionary and then the layer states dictionary's dxf data. It returns and calculates the layer list in one go through the vl-remove-if-not ... portion. I've added a wildcard matching to it as well, and then as added bonus the layerstate-delete-wcmatch which you simply call thus from anywhere:

(layerstate-delete-wcmatch "*")

The wildcard works as usual, e.g. "my*" would match states named MyState, My1, mY3, MY, my, etc.

Posted

It would probably be possible to combine the 2 vl-remove-if-not's into one, but whether that's going to be much more efficient I'm not sure. Anyhow, if efficiency is a big issue with layerstate names, you've got lotts of other problems! :cry:

Posted
Also how can I confirm that the layerstate restore options always have "apply properties as viewport" unchecked in a scrip file?

(layerstate-import "I:\\DRAWINGS\\090-0056 LFMP\\Common\\Layer_States\\ELLS.las")
(layerstate-restore "ELLS" nil)

No I don't think this is possible: From the help on this function the 3rd parameter allows a bitmapped value of 4 to use VP Overrides, but only if the 2nd parameter is not nil and contains the ename of the viewport. So if you send nil through to it, it "should" restore the state throughout the drawing without using VP overrides ... at least that's how I understand it. Only if you pass a VP's ename to it can you turn on VP Overrides for that one restore only by adding 4 to the 3rd parameter.
Posted
It would probably be possible to combine the 2 vl-remove-if-not's into one, but whether that's going to be much more efficient I'm not sure.

 

(defun layerstate-list ( match / temp )
 (if
   (and
     (setq temp  (entget (tblobjname "LAYER" "0")))
     (setq temp  (entget (cdr (assoc 330 temp))))
     (setq temp  (cdr (assoc 360 temp)))
     (setq temp  (dictsearch temp "ACAD_LAYERSTATES"))
     (setq match (strcase match))
   )
   (mapcar 'cdr
     (vl-remove-if-not
      '(lambda ( pair ) (and (= (car pair) 3) (wcmatch (strcase (cdr pair)) match))) temp
     )
   )
 )
)

But I doubt it'd increase the efficiency by too much...

 

Or there's the VL approach:

 

(defun layerstate-list ( layercollection match / dict name result ) (vl-load-com)
 (if
   (and (eq :vlax-true (vla-get-hasextensiondictionary layercollection))
     (not
       (vl-catch-all-error-p
         (setq dict
           (vl-catch-all-apply 'vla-item
             (list (vla-getextensiondictionary layercollection) "ACAD_LAYERSTATES")
           )
         )
       )
     )
   )
   (vlax-for state dict
     (if (wcmatch (setq name (vla-get-name state)) match)
       (setq result (cons name result))
     )
   )
 )
 (reverse result)
)

(layerstate-list (vla-get-layers (vla-get-ActiveDocument (vlax-get-acad-object))) "*")

Which will be inevitably slower.

Posted

Thanks yes Lee - going with the ActiveX to obtain the layerstate dictionary is probably quicker than the DXF path. Though it's a once-off thing, so extremely unlikely that it's going to save more than a few micro-seconds on the total. And even (as you've also noted) combining the 2 vl-remove's doesn't do all that much anyway - all I can see where it helps is that the (mapcar 'cdr ...) may have a shorter list to go through in some cases. Of course as I've mentioned: if you have so many states as to start waiting (more than about a second) for that function to calculate - then there's something seriously wrong with your drawing. That function should be able to get more than 100 states within a second.

 

BTW, I've noticed you usually just use a quoted lambda. Is there any true benefit in the (function (lambda ... method above a normal '(lambda ... ? Or is it mainly less typing? :wink:

Posted

You could step though it with foreach instead of twice with mapcar and vl-remove-if. You could combine a lot of your and checks (especially since you are checking if an entget is valid, when it will either work or crash the program) giving that a lot of those checks aren't necessary since they'll always exist in a drawing.....but as it's been said, it wouldn't give you that much of an increase.

Posted
BTW, I've noticed you usually just use a quoted lambda. Is there any true benefit in the (function (lambda ... method above a normal '(lambda ... ? Or is it mainly less typing? :wink:

 

I switch from one to the other from time to time. In the past I've seen a minor increase in performance through the use of 'function' since this perhaps removes some legwork for the interpreter, but I believe the difference is very small indeed.

 

_$ (setq l '(1 2 3 4 5))
_$ (repeat 8 (setq l (append l l)))
_$ (setq m (reverse l))

_$ (Benchmark '((mapcar '+ l m) (mapcar (function +) l m)))

Benchmarking ................Elapsed milliseconds / relative speed for 8192 iteration(s):

   (MAPCAR (FUNCTION +) L M).....1981 / 1 <fastest>
   (MAPCAR (QUOTE +) L M)........1982 / 1 <slowest>

Posted

With Alan's suggestions:

 

(defun layerstate-list4 ( match / temp result ) (setq match (strcase match))
 
 (if (setq temp  (cdr (assoc 360 (entget (cdr (assoc 330 (entget (tblobjname "LAYER" "0"))))))))
   (foreach pair (dictsearch temp "ACAD_LAYERSTATES")
     (if (and (= (car pair) 3) (wcmatch (strcase (cdr pair)) match))
       (setq result (cons (cdr pair) result))
     )
   )
 )
 (reverse result)
)

A quick test on a drawing with 10 Layerstates:

 

layerstate-list1 = Irneb's first post

layerstate-list2 = My mods to Irneb's code

layerstate-list3 = My VL method

layerstate-list4 = Irneb's code with Alan's suggestions

 

As predicted the VL method is much slower...

 

_$ (setq lay (vla-get-layers (vla-get-activedocument (vlax-get-acad-object))))

_$ (Benchmark '((layerstate-list1"*")(layerstate-list2 "*")(layerstate-list3 lay "*")(layerstate-list4 "*")))
Benchmarking ................Elapsed milliseconds / relative speed for 8192 iteration(s):

   (LAYERSTATE-LIST1 "*").........1201 / 4.3 <fastest>
   (LAYERSTATE-LIST4 "*").........1264 / 4.08
   (LAYERSTATE-LIST2 "*").........1607 / 3.21
   (LAYERSTATE-LIST3 LAY "*").....5163 / 1 <slowest>

On the same drawing using an optimised FAS:

 

_$ (Benchmark '((layerstate-list1"*")(layerstate-list2 "*")(layerstate-list3 lay "*")(layerstate-list4 "*")))
Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s):

   (LAYERSTATE-LIST4 "*")..........1747 / 5.75 <fastest>
   (LAYERSTATE-LIST1 "*")..........2059 / 4.88
   (LAYERSTATE-LIST2 "*")..........3027 / 3.32
   (LAYERSTATE-LIST3 LAY "*").....10046 / 1 <slowest>

Posted

Interesting that a double vl-remove-if step through and all the erroneous and checks is faster than foreach and cons. I wonder if reverse (slow) is what held it back?

 

Thanks for running the checks, Lee.

Posted
Interesting that a double vl-remove-if step through and all the erroneous and checks is faster than foreach and cons. I wonder if reverse (slow) is what held it back?

 

Exactly what I was thinking - although the performance of the 'foreach' shines through with the compiled test (updated above).

 

Thanks for running the checks, Lee.

 

No worries mate :)

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