Last week, someone posted a question to a customer forum
for CoCreate Modeling (aka PTC Creo Elements/Direct Modeling),
providing the perfect excuse for me to dabble with LISP again.
The question centered on how to expand the results of the API call
sd-inq-vp-drawlist-objects
which returns a list of all
currently visible objects.
In the example to the right, the following objects are checked, and are therefore visible:
- Assembly "a1"
- Part "cone" in assembly "a1"
- Part "cube" in assembly "a1"
- Part "backplate" in assembly "act_assy"
- Part "housing" in assembly "act_assy"
- Part "pistonhead" in assembly "act_assy/pst_subassy"
- Part "shaft" in assembly "act_assy/pst_subassy"
To reduce the amount of data it has to return,
sd-inq-vp-drawlist-objects
"compresses" its result as follows:
- If all objects below an assembly are checked (=visible), only the assembly is returned
- In partially visible assemblies, visible objects are returned individually
So in our example,
sd-inq-vp-drawlist-objects
would return a list containing:
- /a1
- /act_assy/backplate
- /act_assy/housing
- /act_assy/pst_subassy
This representation is perfectly fine for many purposes, but in the specific circumstances of the forum post, the user
needed a way to "uncompress" the result, and wasn't interested in the assemblies, only in parts. So in the given example,
the desired output would have been:
- /a1/cone
- /a1/cube
- /act_assy/backplate
- /act_assy/housing
- /act_assy/pst_subassy/piston-head
- /act_assy/pst_subassy/shaft
Assembly structures can be highly nested, of course, and so a recursive solution is needed.
My first solution revisited an approach I used in
a
previous blog post
which discussed how to recurse over a hierarchy of objects and build a flat list from it.
(in-package :de.clausbrod.expanddrawlist)
(use-package :oli)
(defun flatten-assembly-mapcan(node)
(cons node
(mapcan #'flatten-assembly-mapcan (sd-inq-obj-children node))))
(defun expand-objects(objects)
(loop for obj in objects
nconc (remove-if-not #'sd-inq-part-p (flatten-assembly-mapcan obj))))
(defun show-part(p)
(display (sd-inq-obj-pathname p)))
(let ((displayed-objects (sd-inq-vp-drawlist-objects (sd-inq-current-vp))))
(mapc #'show-part (expand-objects displayed-objects)))
This worked and returns lists as described above. However, if you're actually not really interested in those intermediate lists, but instead simply
want to visit all visible parts in the assembly tree and execute some action for each of those parts, then the problem can be solved more succinctly:
(in-package :de.clausbrod.expanddrawlist)
(use-package :oli)
(defun show-part(p)
(display (sd-inq-obj-pathname p)))
(defun visit-parts(obj)
(if (sd-inq-part-p obj)
(show-part obj)
(mapc #'visit-parts (sd-inq-obj-children obj))))
(let ((displayed-objects (sd-inq-vp-drawlist-objects (sd-inq-current-vp))))
(mapc #'visit-parts displayed-objects)))