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

Wires from Edges? Suggestions?

Evan_ReeseEvan_Reese Member Posts: 2,065 PRO
I'm trying to create a FS that will create a wireframe from the edges of a solid body (with only planar faces). What I want is for it to start like the body on the left and end up like the part on the right:

Here's a link to this model. I think that the FS operation would just automate the way I did this in the feature tree of the part called "Desired Outcome". Here's a summary of the basic operation steps I'm imagining. I'm new to FS and coding in general, and this is an exercise to help me learn, so let me know if this logic makes sense:

UI inputs:
  • select body
  • wire depth
  • wire width
Operation function:
  1. select all faces belonging to body
  2. new sketch on those faces (this is where i'm a bit lost. How do I select the planar faces from the body and sketch on them?)
  3. offset the edges of the faces by the "wire width" 
  4. solve sketches
  5. hollow the part with opShell = to "wire depth"
  6. opExtrude all of the sketches into the part by "wire depth" (possibly add 1-2 mm as a buffer)
  7. opBoolean to subtract it all from the body
  8. delete all of the sketches.
Any and all code advice is very welcome. I've got plenty to learn and doing something hands on like this will help the learning stick.

Evan Reese / Principal and Industrial Designer with Ovyl
Website: ovyl.io

Comments

  • Options
    kevin_o_toole_1kevin_o_toole_1 Onshape Employees, Developers, HDM Posts: 565
    edited December 2016
    Looks FeatureScript is definitely the right tool for the job here, and this looks like a great starter project!

    I think your current plan going to run into some trouble at step 3: Using/offsetting sketch geometry. Right now, both "Use" and "offset" in Onshape are functions that generate FeatureScript (creating sketch entities and constraints), rather than calling a FeatureScript function. This means it will actually be pretty non-trivial to make a good use/offset tool in FeatureScript right now. Eventually, we do want to make built-in functions that do this stuff (so users don't have to), but that addition has yet to be prioritized.

    In the meantime, FeatureScripts 3D tools are a bit more fleshed out than its 2D tools, and you should be able to get the geometry you want without making sketches at all. A good possibility I can see:
    1. Query all faces of the body
    2. For each face, extrude it inward by the wire depth, remembering each extrude body
    3. For each extrude, thicken its non-cap faces inward by the wire width
    4. Subtract that thicken result from the extrude (giving you the desired offset punch-out)
    5. Shell the original solid
    6. Subtract the shrunken extrudes
    Does that get you the result you want?

  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,065 PRO
    @kevin_o_toole_1
    thanks for the answer. I think that makes a lot of sense. I'll probably try what you're describing manually first to wrap my head around the function, but it sounds like it would produce an identical result. Then I'll take a stab as writing the script and probably post back here when I hit a snag.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,065 PRO
    edited December 2016
    @kevin_o_toole_1

    I think where I'm getting hung up is in some of the basic aspects of FS (and code in general). I understand, at a high level, that I want to query the faces of the body, then extrude them in a direction normal to the faces, but I don't know how to set that up. I understand how to extrude one face in one direction, but I'm having a hard time understanding how to extrude all faces in different directions.

    Am I anywhere close with this?

    // variables

        var face is BodyType = qOwnedByBody(definition.body, EntityType.FACE);
        
        var faceNormal is Vector = evPlane(context, {
                    "face" : face
            }).normal;

    // extrude faces inward

        opExtrude(context, id + "extrude1", {
                "entities" : face,
                "direction" : faceNormal,
                "endBound" : BoundingType.BLIND,
                "endDepth" : definition.depth
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,065 PRO
    Another thought I've had is to use opThicken instead of opExtrude. Is there a way to access boolean options inside of opThicken? By default, everything is joined, but I'd like each separate face to produce a separate body.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    kevin_o_toole_1kevin_o_toole_1 Onshape Employees, Developers, HDM Posts: 565
    edited December 2016
    You're on the right track. The missing piece is evaluateQuery and a for loop, giving you:
    // variables
        var faceQuery is Query = qOwnedByBody(definition.body, EntityType.FACE);
        var faces is array = evaluateQuery(context, faceQuery);
        
        for (var i = 0; i < size(faces); i += 1) {
            var face is Query = faces[i];
            var faceNormal is Vector = evPlane(context, {
                        "face" : face
            }).normal;        
            
            // extrude face inward
            opExtrude(context, id + i + "extrude", {
                    "entities" : face,
                    "direction" : -faceNormal,
                    "endBound" : BoundingType.BLIND,
                    "endDepth" : definition.depth
            });
        }
        debug(context, qCreatedBy(id + 3 + "extrude"));    
    Conceptually, we're making a different extrude in a different direction for every face. We split out the Query into array of individual faces with evaluateQuery.

    I like to think of a Query like faceQuery as an order form for "all the faces belonging to this body" (which can change if your feature modifies that body), while the evaluated query is a list of entities in the context that satisfy the Query right now. Most of the time you can just pass around queries, but if you need the list (to see how big it is or loop through it), evaluateQuery is the tool for the job.

  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,065 PRO
    @kevin_o_toole_1

    thanks, the evaluateQuery and "for loop" are what I needed for the extrude (or thicken). I've got that working well now. This leads me to some new questions:

    1. I've got an extrude and I want the non-cap faces, as you said. I'm trying to find them like this, but it seems to be missing something to work:

    <div>var allFaces is Query = qCreatedBy(id + "extrude1", EntityType.FACE);</div><div>&nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; var capFaces is Query = qUnion([</div><div>&nbsp; &nbsp; &nbsp; &nbsp; qCapEntity(id + "extrude1", true),</div><div>&nbsp; &nbsp; &nbsp; &nbsp; qCapEntity(id + "extrude1", false)]</div><div>&nbsp; &nbsp; &nbsp; &nbsp; );</div><div>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; var nonCapFaces = qSubtraction(allFaces , capFaces);</div>


    2. the "qCapEntity" function says it can do extrudes, revolves, sweeps, and lofts. could I do something like this for opThicken? In thinking about this feature more, I would like to be able to use it for bodies with non-planar faces as well. Thicken would give me that.

    by the way, I appreciate your instructive style. You're giving enough info for me to explore and be challenged, but not just leaving me hanging by telling me to "read the documentation". That's helpful. (though, please point me to relevant documentation, as you have been).
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,065 PRO
    edited December 2016
    3. my code formatting looks funny on my end (carriage returns are missing in the code, making it run off the page. If it does to you too, then please let me know how to do that better too.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    kevin_o_toole_1kevin_o_toole_1 Onshape Employees, Developers, HDM Posts: 565
    1. The format of the query looks right to me. Perhaps the ids are off? Make sure it matches the unique id of the feature creating the faces you need (i.e. id + i + "extrude"). To check, my go-to option is to debug a query to see what it resolves to (like in the tutorial).

    2. opExtrude works on non-planar faces as well, but it does something different from opThicken: opThicken makes its side faces normal to the surface, while extrude makes side faces parallel to the extrude direction (a direction you'd have to infer with evFaceTangentPlane replacing evPlane)... okay I think I've just convinced myself that opThicken is the right choice, but as the feature designer it's your call what geometry you want in the non-planar case.

      We do track cap entities for opThickens, so that should work the same. 

    3. Yeah, looks funny to me as well. I've had luck with pressing the "code" format button on a blank line, then pasting code onto that line (rather than highlighting-formatting code).

  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,065 PRO
    Thanks again! I'm taking some time to convert it to opThicken instead of opExtrude, because I agree with you, that it's going to produce more predictable results. The main reason I didn't go that route is because the qCapEntity didn't list it as compatible. I'm sure to have more questions as I go on. I will post back once I do.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,065 PRO
    edited December 2016
    @kevin_o_toole_1

    I'm this close! I've got the for-loop working right. I'm looping opThicken on each face and looping opOffsetFace on all of the non-cap faces of the thickened bodies. I've also got the main body hollowing out perfectly. Works like a charm. (here's the document link, btw)

    My last hangup is in finding the right way to query those bodies that I made with the for-loop. I'm trying qCreatedBy and putting the feature IDs in, but nothing seems to happen. Debugging that query turns up nothing either (further indicating that nothing is happening). Does this have something to do with the "i" variable in the for-loop giving each loop of the opThicken a different ID? If so, I'm not sure how to access them. Or is there a sneakier query to get at them?

    Thanks for all of your help so far!
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 1,176
     That's exactly the problem: you're creating thicken operations with id + 1 + "thicken1", id + 2 + "thicken1", etc.  There's no way to query for all of them at once (without constructing the query in a for loop as well) and id + "thicken1" will not find them (ids are a hierarchy).
    The way to solve the problem is to use id + "thickens" + i + "thicken1" instead of id + i + "thicken1" and then qCreatedBy(id + "thickens") should resolve to the lot.
    Ilya Baran \ VP, Architecture and FeatureScript \ Onshape Inc
  • Options
    Jake_RosenfeldJake_Rosenfeld Moderator, Onshape Employees, Developers Posts: 1,646
    edited December 2016
    Hi Evan,

    Try changing the opThicken part of your code to this:
    var relevantGeometryQueries = [];
    
    for(all the relevant faces) {
        var thickenId = id + ("thicken" ~ i);
    
        // do the thicken and use thickenId to collect those faces for offsetFace
    
        var offsetFaceId = id + ("offsetFace" ~ i);
    
        // do the offsetFace
    
        relevantGeometryQueries = append(relevantGeometryQueries, qCreatedBy(thickenId, EntityType.BODY));<br>    relevantGeometryQueries = append(relevantGeometryQueries, qCreatedBy(offsetFaceId, EntityType.BODY));<br>}
    
    relevantGeometry = qUnion(relevantGeometryQueries);
    
    // use relevantGeometry in opBoolean
    Basically just collect all your bodies you are creating as you create them.  Additionally the <id + ("thicken" ~ i)> thing is a minor tweak to simplify your ids a bit (the ~ operator is a string append).  You may not need to add the qCreatedBy(offsetFaceId, ...) to relevantGeometryQueries, I'm not sure if offset face alters bodies or creates new ones internally.  Experiment and you'll probably be able to get away with just one of those.

    Hope this helps!

    EDIT: Thanks Ilya! That's much easier than my way.
    Jake Rosenfeld - Modeling Team
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,065 PRO
    edited December 2016
    @Jake_Rosenfeld  @ilya_baran

    Thank you both for the input.

    Jake, you're saying that by using ("thicken" ~ i) I'm essentially going to output "thicken1", "thicken2", thicken3" etc. am I understanding the string append right?

    Ilya, would it be right to think of the id hierarchy as similar to a basic set of folders and sub-folders? Is that a useful mental model?

    Also, I made the feature id to be
    id + "thickens" + i + "thicken1"
    Then I used
    debug(context, qCreatedBy(id + "thickens", EntityType.BODY));
    When I debug inside of the for-loop I get just one instance of the opThicken (which I think makes sense. It's just getting one instance of the loop, right?). If I debug outside of the for-loop, I get nothing. Thoughts?
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 1,176
    You're really close!

    The problem now is that opOffsetFace is failing (you can see that as the error) because the id there is still id + i + "offsetFace1".  If you change it to id + "thickens" + i + "offsetFace1", it'll work (and because opOffsetFace does not create any bodies, your query will be fine).  The folders are a useful mental model -- except that (and this is the problem in this case) once you "close" a folder by starting to put something into a different folder, you cannot reopen it again...
    Ilya Baran \ VP, Architecture and FeatureScript \ Onshape Inc
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,065 PRO
    yahtzee! it's working. Everything from here on is just polish. 

    I may eventually add a button to link width and depth dimensions and set dimension defaults to 5mm instead of 25mm, which is a good starting place for 3D printing. I'll work on those a bit in the next few days and report back if I get stuck. Thanks, folks!
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,065 PRO
    Just wanted to update everyone who was so helpful in this thread. I polished up the UI a bit on the Wires feature (as mentioned above). I also used what I learned here to write a complementary script called Stellate. It just batch extrudes selected faces with a specified draft. With a bit of recursion (just by repeating it a few times in the feature tree), it's possible to get some pretty complex shapes very quickly. I can't think of any real-world applications for it other than just 3D printing frivolous baubles, but hey, it's been a blast. Check it out! This started as a cube.


    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    Jake_RosenfeldJake_Rosenfeld Moderator, Onshape Employees, Developers Posts: 1,646
    Awesome feature Evan!!
    Jake Rosenfeld - Modeling Team
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,065 PRO
    @Jake_Rosenfeld
    thanks for the help!
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
Sign In or Register to comment.