Welcome to the Onshape forum! Ask questions and join in the discussions about everything Onshape.

First time visiting? Here are some places to start:
  1. Looking for a certain topic? Check out the categories filter or use Search (upper right).
  2. Need support? Ask a question to our Community Support category.
  3. Please submit support tickets for bugs but you can request improvements in the Product Feedback category.
  4. Be respectful, on topic and if you see a problem, Flag it.

If you would like to contact our Community Manager personally, feel free to send a private message or an email.

Nuances of Featurescript 'FeatureList' Inputs

Jonathan_HutchinsonJonathan_Hutchinson Member Posts: 91 PRO
I've produced a couple of utility functions that work with features that output (intentionally) multiple unjoined bodies. I'm finding I'm sometimes not able to reliably work with the functions I've made though, as the bodies I am expecting aren't fed in correctly, or are a little bit non-intuitive.

I've attached the pictures below, apologies as they seem to have blown up massively. You can see the slightly odd way that the 'Feature pattern' distinguishes the input. The bodies output from my 'Mirror Custom' is considered to be everything except for the top face, but once I pick that extra face too (which is considered to be the output of a different feature, for some reason) then the linear pattern works.

This seems to be what happens in a few of my featurescripts that take in the FeatureList type, so I'm wondering how I can tune some of these up to work a bit more as expected. Other examples of what I've produced are:

-Transform all the outputs/bodies of a feature, or all 'involved' in a feature I suppose
-Perform a Union of all the outputs of a feature
-Subtract from all the outputs of a feature with another selected body

Appreciate I might need to expand to explain a little bit better still.

Comments

  • Alex_KempenAlex_Kempen Member Posts: 248 EDU
    FeatureList selections tend to be pretty rare - the only uses I can think of in the standard library is with sketches (where entities tend to be very stable) and with feature pattern (which uses context + getRemainderPatternTransform to enable feature patterns). Are there any particular reasons you're using FeatureList over traditional queries?
    CS Student at UT Dallas
    Alex.Kempen@utdallas.edu
    Check out my FeatureScripts here:



  • Jonathan_HutchinsonJonathan_Hutchinson Member Posts: 91 PRO
    Let me see if I can describe it suitably, so that makes sense.

    A repeated use case I am creating is to create a hollow cylinder with thickness, and for it to be divided into a number of convex elements - quite similar to the ones you see above. I have a custom feature which changes the number of divisions for this, ad hoc. Then, let's say I want to array that decomposed cylinder into multiple cylinders. This is handily something I can do with Feature Pattern; I select one of the entities from my decompose feature, and Onshape is being intelligent and performing an action based on the output bodies of a feature above it.

    That led me to generate a couple of other examples; mainly I've needed a transform and a mirror to do the same thing; respect whatever the output of the decompose feature, whether it be 6 segments or 10, and transform/mirror all of them in whichever way I am choosing. Does that makse sense?

    The requirement to keep all these separate bodies is liekly the main reason I've approached it this way, and I do think it's quite elegant myself. But I feel like I'm probably just one or two lines away from defining it as correctly as it should be - with the above example images which is very nearly working correctly but also just a tad buggy.

    If I'm thinking rightly, using just a qCreatedBy wouldn't allow me to get the precise control of what I want to obtain the bodies that a feature, based on the selection of an outputted entity, produced. Even though I'm using qCreatedBy below, I'm using the FeatureList to enable me to get all bodies created by a feature as a selection.

    An example - have I maybe overcomplicated something here? This would delete all the bodies created by a feature, based on the selection of one of the bodies.
    <div>annotation { "Feature Type Name" : "Delete Feature Bodies",</div><div>&nbsp; &nbsp; &nbsp; &nbsp; "Feature Description" : "Returns the feature that created an entity, and deletes all related entities created by that feature instance." }</div><div>export const delFeatBodies = defineFeature(function(context is Context, id is Id, definition is map)</div><div>&nbsp; &nbsp; precondition</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; annotation { "Name" : "Select Feature/s to Delete" }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; definition.featureListObj is FeatureList;</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; var featureSelection = qCreatedBy(definition.featureListObj, EntityType.BODY);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; // var featureBodySelection = qBodyType(featureSelection, BodyType.SOLID);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; var featureOwnerBodies = qOwnerBody(featureSelection);</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; debug(context, featureOwnerBodies, DebugColor.RED);</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; forEachEntity(context, id + "DeleteBody", featureOwnerBodies, function(entity is Query, id is Id)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; opDeleteBodies(context, id + "deleteBodies1", {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "entities" : entity</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });</div><div>&nbsp; &nbsp; });</div><div></div>

  • EvanReeseEvanReese Member, Mentor Posts: 2,144 ✭✭✭✭✭
    @Jonathan_Hutchinson I think you're looking for lastModifyingOperationId() and qCreatedBy(). Here's an example showing how you can use these to have the user choose a body, and FeatureScript will select all of the other bodies made by the feature that made the body.


    Evan Reese
  • Jonathan_HutchinsonJonathan_Hutchinson Member Posts: 91 PRO
    Hi Evan, that sounds promising. I think I still have a bit of confusion here though. Is your example picking up everything created by multiple hits of an extrude feature? This is where what I'm looking for might be different. I would want, for example, all the bodies created from a specific hit of an extrude feature rather than globally. This is how I'm sort of gathering up related bodies that describe some part.

    I guess the question, from a curiosoity point of view, would then be what does the FeatureList type do specifically differently? Maybe I need to do more digging into the specifics of how OnShape features work again. It's sort of a chain of id's IIRC.

    So I tried to add some code but that failed spectacularly  :# also how did you add your gif? Does it have to be hosted? Because I did want to do that to better explain my results!
  • Alex_KempenAlex_Kempen Member Posts: 248 EDU
    If your issue can be rephrased as "you have a query for some faces on a body, and you need a query for all faces on that body", then a relatively straightforward solution is to use qOwnerBody followed by qOwnedByBody to flood a query for a single face across to the rest of the body. For example, given a query for a single face on a body, the following should give you all faces on that body:
    const allFaces = qOwnerBody(face)->qOwnedByBody(EntityType.FACE);
    To improve performance, you may also wish to utilize the following procedure:
    1. Collect all owner bodies of your input faces (storing the results in an array)
    2. de-duplicate the array using qUnion
    3. Call qOwnedByBody on each remaining body to get the final faces
    CS Student at UT Dallas
    Alex.Kempen@utdallas.edu
    Check out my FeatureScripts here:



  • EvanReeseEvanReese Member, Mentor Posts: 2,144 ✭✭✭✭✭
    Hi Evan, that sounds promising. I think I still have a bit of confusion here though. Is your example picking up everything created by multiple hits of an extrude feature? This is where what I'm looking for might be different. I would want, for example, all the bodies created from a specific hit of an extrude feature rather than globally. This is how I'm sort of gathering up related bodies that describe some part.

    I guess the question, from a curiosoity point of view, would then be what does the FeatureList type do specifically differently? Maybe I need to do more digging into the specifics of how OnShape features work again. It's sort of a chain of id's IIRC.

    So I tried to add some code but that failed spectacularly  :# also how did you add your gif? Does it have to be hosted? Because I did want to do that to better explain my results!
    My demo feature selects every body that's made by one extrude feature, even though I'm only selecting one of those bodies. Is that your goal? I'm not sure what you mean by "multiple hits" of an extrude feature, but I imagine it means just multiple extrudes? If you want to choose features instead of bodies for this to work, you can do that too. Here's an example. https://cad.onshape.com/documents/e2f27c35c2fdfae297f5ec5e/w/04a45749a023927cb26dc3e9/e/fc4fd20d069ab6fad15b5051. I added some comments to the code to help orient you.

    For the gif, you can just drag and drop it into the message.
    Evan Reese
  • Jonathan_HutchinsonJonathan_Hutchinson Member Posts: 91 PRO
    @Evan_Reese, ah gotcha, that's what I meant yeah - multiple hits just meaning multiple actual enactions of an extrude feature. For example, 10 sketch face regions that are 10mm deep, 5 that are 6mm deep etc, thus multiple features in the tree. Thank you for that nice sample.

    I don't think I've come across the '->' ; is this ternary-ish or an inline lambda from what my glance at the docs reveals so far? Is the below essentialy passing what the first half evaluates to into after the arrow?

    &nbsp;keys(definition.featureList)[0][0]->makeId()

    @Alex_Kempen I think you're probably going to be pretty close - so I will try that. The phrasing is close, maybe more like:
    "You have a query for some faces on many bodies, related to output from a feature, and you need a query for just the solid bodies that form them".

    Could you explain the 'de-duplicate the array using qUnion' step? Phew, all these nested queries sure can start to get out of hand can't they. Thank you both again for assisting here.
  • NeilCookeNeilCooke Moderator, Onshape Employees Posts: 5,688
    -> inserts the result from the statement before it, into the first parameter of the statement after. Works on everything but is most useful when chaining queries. Makes them much easier to read. 
    Senior Director, Technical Services, EMEAI
  • Alex_KempenAlex_Kempen Member Posts: 248 EDU
    edited June 2023
    If you just want bodies, then you can just use qOwnerBody on any faces you have. You only need a following qOwnedBy if you want to get back to faces afterwards.

    The arrow operator (->) passes the value on the left as the first value of the function call on the right:
    const two = [3, 4]->size(); // equivalent to size([3, 4]);
    qUnion has the neat behavior of removing redundant queries from an array of queries. Here's a simple example:
    https://cad.onshape.com/documents/00dd11dabe44da2db458f898/w/6c20cd994b174cc99668701f/e/fcfef0ec86c439d44f5dbb3d
    Note also the use of map to create an array of individual queries before a final call to qUnion. This is more performant than calling qUnion as you go, e.g. myQuery = qUnion(myQuery, newQuery), as array operations are faster than qUnion (although the performance difference is quite marginal until you get into the hundred or thousand plus range).
    CS Student at UT Dallas
    Alex.Kempen@utdallas.edu
    Check out my FeatureScripts here:



  • Jonathan_HutchinsonJonathan_Hutchinson Member Posts: 91 PRO
    edited June 2023
    Chaining in cases like the below, is this valid?

    qCreatedBy(definition.featureListTools, EntityType.BODY)->qOwnerBody()->qUnion()

  • Alex_KempenAlex_Kempen Member Posts: 248 EDU
    The code is valid. Note, however, that qOwnerBody() will only process the first entity the passed in query resolves to (if there is more than one). For that reason, you will likely want to evalute your qCreatedBy query into an array of bodies (using `evaluteQuery`), then iterate over each body and call qOwnerBody(), collecting results into an array. Call qUnion on the resulting array to deduplicate the result and make it into a query again.

    See the example I linked in my above post for a concrete example of this implementation.
    CS Student at UT Dallas
    Alex.Kempen@utdallas.edu
    Check out my FeatureScripts here:



  • Jonathan_HutchinsonJonathan_Hutchinson Member Posts: 91 PRO
    I seem to have hit a roadblock here that might have been predicted. I want to call one of the functions I made which takes as an argument/parameter a FeatureList. From where I'm calling, I only have a query (qCreatedBy) which gives me some solid entities that I would pass through. But, I suppose I've hamstrung myself as a FeatureList is wanted.

    Is it going to be simplest to change my original function to allow queries as input? Or can I go backwards from a query to a featureList?
Sign In or Register to comment.