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.

Options

opLoft errors

konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
edited October 2020 in FeatureScript
I'm making feature which generates a number of cross sections, obtained from surface intersections, they are then appended to the list and passed to the opLoft. And I keep getting 2 types of errors in the console:
1) @opLoft: LOFT_SELECT_PROFILES in case when i simply pass a list of queries for generated profile wire bodies
2) 
@opLoft: LOFT_DIRECTION_ERROR in case when I pass for profileSubqueries evaluated query of qUnion for list of profile queries: "profileSubqueries" : evaluateQuery(context, qUnion(profiles))
But I should notice that in second case the opLoft worked as expected and made a surface in another test situation. The strangest thing is that I can still build a loft through my generated wires with builtin part studio feature with no errors.
Here is the feature: https://cad.onshape.com/documents/e0e6d57303f343d9a489112e/w/6aa4adcacac20a0b5016c188/e/eeeff94d3c1ff3b4f68b8be2
And test cases are in Part studio 0 and Part studio 1.
I have a suspicion that error @opLoft: LOFT_DIRECTION_ERROR only occurs for open contours

Comments

  • Options
    Jacob_CorderJacob_Corder Member Posts: 126 PRO
    what happens if you do this?

    change line 93 to profiles = append(profiles, makeRobustQuery(context,qBodyType(qCreatedBy(id + ("crossSection" ~ i), EntityType.BODY),BodyType.WIRE);

    I am wondering if the boolean is not resolving the entities for you. however, when I test this, it resolves fine.
    I have to use makeRobustQuery sometimes when I am using Extend

    calling qUnion(profiles) seems like it may order the entities wrong for you. I would avoid that if at all possible. 

    Another option would be to just use the evPathTransforms code in Path utils
    https://cad.onshape.com/documents/f902670516b3542414e3ab98/v/a0af217573ceb4be567f23f0/e/81858a9ae2c1683847d4a5b0

    Then, transform and copy the wire body extracted from the boolean intersection on line 86. this opBoolean on line 86 would only need to be executed once, then opPattern the results with the results from evPathTransforms.

    When you do figure this out, please share. I often have loft issues that are hard to determine the culprit.
  • Options
    konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    @Jacob_Corder thanks for the ideas, just tested it with makeRobustQuery() but it sadly returns LOFT_SELECT_PROFILES error for both open and closed profiles. The thing in this feature is that the cross section obtained from boolean intersection varies along path so i get the outer envelope of body movement. And if don't find the solution with pathPattern i would have to use more low-level code with evPathTransforms
  • Options
    Jacob_CorderJacob_Corder Member Posts: 126 PRO
    @konstantin_shiriazdanov

    have you tried using loft instead of opLoft? 
    there are some things that loft will fix for you. I generally use loft unless its a simple loft. 
    I cannot see why this is failing to be honest. I would try loft instead of opLoft.  
  • Options
    konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    edited October 2020
    I'm lost now in these queries :s Packed profile query into the map format that loft() accepts: 
    profiles = append(profiles,
                            { "wireProfileEntities" : qCreatedBy(id + ("crossSection" ~ i), EntityType.BODY)->qBodyType(BodyType.WIRE) }
                        );
    trying to feed it to the loft:
    loft(context, id + "mainLoft", {
                            "bodyType" : ToolBodyType.SURFACE,
                            "wireProfilesArray" : profiles,
                        });
    and the error in console is
    Precondition of replaceWireSubQueriesWithDependencies failed (query.subqueries is array)
    I have a number of features which build a loft through the cross sections generated by all kinds of patterns but something really weird is going with these cross sections from boolean intersection.

    and if I attach to the each profile query "subqueries" field with an empty array loft() shows old good
    LOFT_SELECT_PROFILES and LOFT_DIRECTION errors
  • Options
    Jacob_CorderJacob_Corder Member Posts: 126 PRO
    profiles = append(profiles,
                            { "wireProfileEntities" : qUnion([qCreatedBy(id + ("crossSection" ~ i), EntityType.BODY)->qBodyType(BodyType.WIRE)]) }
                        );
  • Options
    Jacob_CorderJacob_Corder Member Posts: 126 PRO
    Perhapse instead of passing the edges created from the Intersection.  use opExtractWires on those edges. it's worth a try.

    to be honest, i didn't even know boolean intersection of 2 sheets generated curves.  I normally use opSplitFace to get those curves.  I have tested the boolean intersection on a face, and the result was actually 3 different edges which will really make a loft mad if you do not use guide curves. 
  • Options
    konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    edited October 2020
    profiles = append(profiles,
                            { "wireProfileEntities" : qUnion([qCreatedBy(id + ("crossSection" ~ i), EntityType.BODY)->qBodyType(BodyType.WIRE)]) }
                        );
    This gives the result in one test case of three, thanks. Lofts are usually ok with multiedge profiles untill the number of edges in each profile stays the same. I think the problem may be in query evaluation for the result of boolean feature. I beleve opSplit won't work in this case since surface instances may not fully intersect one another.
  • Options
    konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    edited October 2020
    @Jakob_Corder so with opExtractWires it also only created loft for one case of three (for closed cross section case).Will try some another approach tomorrow, thank you.
  • Options
    Jacob_CorderJacob_Corder Member Posts: 126 PRO
    you can use opSplitFace(context, id+"GetIntersectingEdges",{
    "faceTargets" : qOwnedByBody(patternedBody,EntityType.FACE,
    "planeTools" : planeFromEvPathTangentLines
    });
    var newEdges =qCreatedBy(id+"GetIntersectingEdges",EntityType.EDGE);

    Unless you are using the same tool body to create all of the intersections, Is this what microstep does?  I am interested to know why you cannot just use the plane  from evPathTangentLines to split the sections?  I assume you thought of this and the results are not quite correct?
  • Options
    konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    edited October 2020
    you can use opSplitFace(context, id+"GetIntersectingEdges",{
    "faceTargets" : qOwnedByBody(patternedBody,EntityType.FACE,
    "planeTools" : planeFromEvPathTangentLines
    });
    var newEdges =qCreatedBy(id+"GetIntersectingEdges",EntityType.EDGE);

    Unless you are using the same tool body to create all of the intersections, Is this what microstep does?  I am interested to know why you cannot just use the plane  from evPathTangentLines to split the sections?  I assume you thought of this and the results are not quite correct?

    Yes, I tryed opSplitByIsocline using tangent line as direction and even it's resulting cross sections are less accurate then what boolean selfintersection can give. Though I didn't faced any loft problems with these kind of cross sections.
  • Options
    Jacob_CorderJacob_Corder Member Posts: 126 PRO
    edited October 2020
    opSplitByIsocline will definitely give bizarre results.  use opSplitFace, not opSplitByIsocline.  This is how I always extract my edges for lofts. the results are very reliable with almost no breaks in the edges. 

    you will need to take the tangentLines from evPathTangentLines. for every one, call

    opPlane(context, id+"plane"+ i, {
    "plane":plane(tangentLine.origin,tangentLine.normal)
    });

    now split the body faces with this plane.
    opSplitFace(context, id+"GetIntersectingEdges",{
    "faceTargets" : qOwnedByBody(patternedBody,EntityType.FACE,
    "planeTools" : qCreatedBy(id+"plane"+i)
    });
    var newEdges =qCreatedBy(id+"GetIntersectingEdges",EntityType.EDGE);

    plane tools will expand infinitely to ensure the split occurs.

    interestingly enough, the transformation used for each body section, is also using the lines from evPathTangentLines.tangentLines so everything should match up perfectly. 

    So you may call pathPattern 2x and pass the tool once, and the plane body the second time.
  • Options
    konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    edited October 2020
    @Jacob_Corder the results of using isocline contours are actually pretty good:

    They are just not as accurate from geometrical point compared to what boolean self intersection gives. Just beleve me, I know what I'm doing)

  • Options
    Jacob_CorderJacob_Corder Member Posts: 126 PRO
    edited October 2020
    When I use opSplitByIsocline, and the target isn't a revolve type feature,  I tend to get multiple edges when angle is set at 90 degrees or many edges that backtrack on themselves. Its quite amazing how fast the parasolid engine calculates the isocline points. However, the results on complex surfaces is raw and sometimes needs fine tuning.

    I believe you.  I just tested one face with boolean intersection and the results would not be useable for a loft feature as there were 3 edges on one face, where opSplitFace would only give 1 edge. Then the second face i split had 1 edge which would kill a solid loft, a surface loft could be handled using trim sections and trim guides.

  • Options
    konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    I believe you.  I just tested one face with boolean intersection and the results would not be useable for a loft feature as there were 3 edges on one face, where opSplitFace would only give 1 edge. Then the second face i split had 1 edge which would kill a solid loft, a surface loft could be handled using trim sections and trim guides.

    If it was so I wouldn't be able to manually create a loft through wires generated by the feature:


  • Options
    Jacob_CorderJacob_Corder Member Posts: 126 PRO
    @konstantin_shiriazdanov .  I tested a face in one of my models, not yours.  yours do not have broken up edges.  I was checking to see if i could use boolean intersect on 2 surfaces to eliminate some of my code,   

    I have no idea why the loft fails. i would put in an AddDebugEntities, different colors for the first 5 sections to make sure you're actually passing what you want it to pass. again why this would even occur is beyond me.
  • Options
    konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    @Jacob_Corder the order of profiles is correct.

    I feel this is something with non synchronized parametrization direction in open contours, because as for now my test cases fail when I turn closed tool bodies into open and get open cross section contours
  • Options
    konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    edited February 2021
    so if somebody else is interested (@Jacob_Corder) seems like I've found the problem: you have to pass in opLof() an array of profile subqueries and each subquery should already be evaluated before appended to array:
    var profileQuery = qCreatedBy(id + ("crossSection" ~ i), EntityType.BODY);<br>profileQuery = evaluateQuery(context, profileQuery)->qUnion();<br>profiles = append(profiles, profileQuery);
    #SolidBodySweep

  • Options
    Jacob_CorderJacob_Corder Member Posts: 126 PRO
    @konstantin_shiriazdanov. you can also simply use qUnion([qCreatedBy(id + ("crossSection" ~ i)]) or makeRobustQuery(context, qCreatedBy(id + ("crossSection" ~ i)). I think opLoft must look for query.subqueries for some reason as the loft feature wraps profiles in a construction query. Almost like the 2 have been written to work together better than opLoft being more generic.  

    I have found that opLoft is more successful if it follows the qConstructionFilter that you find in the Loft.fs code in function "wrapSubqueriesInConstructionFilter". If you add this to your profile query, it will work better.

    The most robust loft that I have found, is using the onshape loft(context,id,definition). I can pass the same data to loft and opLoft, but opLoft will fail more than loft.  If loft only had a way to select start and end tangent faces, then I could use loft more than opLoft, however for loft.fs, the tangent faces must be adjacent to the first and last sections which is not always possible for me. It is too bad that the general onshape loft feature will not allow us to specify the start faces and end faces as going through the opLoft seems to be highly prone to failures where loft is not. I have even replicated their code to create a more robust definition to pass to opLoft but still, for some odd reason, loft will succeed where opLoft does not. 
  • Options
    konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 1,221 ✭✭✭✭✭
    @Jacob_Corder I bet I've wrapped it in qUnion() earlier and it worked not as stable as when you evaluate the query first explicitly.
    I would also used loft() widely but it is not documented well and as you said it doesn't allow as granular control over ends conditions.
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,064 PRO
    I don't have anything to add here except that I'm excited to see @konstantin_shiriazdanov up to something new! Some of my favorite custom features are yours.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
Sign In or Register to comment.