Hi there,
We have a large LISP project that adds labels to a polyline, like so:
This is very similar to Civil3D, but we had to recreate it in AutoCAD as Civil3D isn't an option for us.
A label consists of multiple entities (MText and Hatch objects). In the NOD, we store the entity handle of the polyline, along with the entity handles of all the label entities, like so:
The polyline handle here would be F011, and the polyline segment would be 0, and inside the XRecord "HANDLE_BOBBEGIN" is the string value 6FB90.
We use entity handles as they remain unchanged when saving, closing and opening a DWG file.
We use a single object reactor to react to changes to the entities. This is created as follows:
(defun REA:CREATE_GLOBAL ()
(vlr-pers
(vlr-object-reactor (list) ; owner registry, defaults to nil
"WATERNET_REACTOR"
'((:vlr-modified . REA:HANDLE_MODIFIED)
(:vlr-erased . REA:HANDLE_ERASED)
(:vlr-unerased . REA:HANDLE_UNERASED)
(:vlr-goodbye . REA:HANDLE_GOODBYE)
(:vlr-modifyUndone . REA:HANDLE_MODIFY_UNDONE)
)
)
)
)
The :vlr-modified event works great. All our handlers look like this (same parameters used by all events):
(defun REA:HANDLE_MODIFIED ($parentVla $reactor $parameters / ...)
...
)
We get the VLA-object of the polyline that is currently reacting, the reactor, and parameters, which is always nil.
We then use (vlax-vla-object->ename) to convert the VLA-object to an ENAME, which allows us to perform (entget) to retrieve the polyline handle. We can then use this handle to update our label entities, retrieved from the NOD.
So, our main goal is to retrieve the entity handle of the current object reacting. However, when using :vlr-erased and :vlr-openedForModify, we run into the following issues:
We fully understand the limitations of LISP, so we are trying to figure out a way to work around this. In the end, we just need to be able to get the entity handle of the VLA-object that is reacting, so that we can retrieve the label data.
We are currently trying an approach where we create a map/dictionary in-memory with all VLA-objects in the drawing and their associated string handle (key-value map). This map is created upon opening the drawing, and kept up to date when new labels are added. I'll update the post if we know more about how well this approach works, but we wanted to ask the community, hoping for a more creative and clever solution to this problem.
Kind regards,
Sem
Solved! Go to Solution.
Solved by sem.keijsper. Go to Solution.
We have figured it out 🙂
Just to update this post in case anyone ever has the same problem; we create a map containing all VLA-object that are added as owners to the object reactor, like so:
(setq $$$vlaObjectMap (mapcar
'(lambda ($owner)
(cons $owner (WTN:VLA_PROP $owner "Handle"))
)
(vlr-owners $$globalReactor)
)
)
$$globalReactor would be your reactor - I have omitted the code that is responsible for creating this reactor, and adding and removing owners from the reactor. You can retrieve the global reactor from the drawing like so:
(setq $$globalReactor (car
(vl-remove-if-not
'(lambda ($reactor)
(= (vlr-data $reactor) "WATERNET_REACTOR")
)
(vlr-pers-list)
)
)
)
WATERNET_REACTOR of course would be different for your own implementation, just use a unique key for vlr-data.
Of course, when you add an owner to the reactor, you also need to add it to the $$$vlaObjectMap:
(defun ...
(setq $$$vlaObjectMap (WTN:ADD_OR_REPLACE
$vlaOwner
(vla-get-Handle $vlaOwner)
$$$vlaObjectMap
)
)
)
(defun WTN:ADD_OR_REPLACE ($key $value $list)
(if (setq $current (assoc $key $list))
(subst (cons $key $value)
$current
$list
)
(append $list (list (cons $key $value)))
)
)
Now, you can retrieve the entity handle during a :vlr-erased event, like so:
(defun REA:HANDLE_ERASED ($parentVla $reactor $parameters)
(setq $handle (cdr (assoc $parentVla $$$vlaObjectMap)))
)
Another key component to make this work is to create a list for VLA-objects that shouldn't be modified, when multiple events are being called. You insert the current $parentVla into this list during :vlr-erased, :vlr-unerased and :vlr-handleModifyUndone. Then, during :vlr-modified, you check if $parentVla is a (member) of this list. If so, you skip the action you usually take, and remove it from the list, like so:
(setq $$$dontModifyList (vl-remove $parentVla $$$dontModifyList))
All of this combined, looks like this:
That's it! Hopefully this can be useful for someone if they ever google this really specific issue 🙂
Feel free to ask more questions about this implementation.
- Sem
PS: In case you are wondering why we prefix our variables with dollar signs, we use the following convention:
; $variable - Local scope variable
; $$variable - Global immutable variable
; $$$variable - Global mutable variable
Can't find what you're looking for? Ask the community or share your knowledge.