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.

FeatureScript Instantiator: Configurations + FeatureScript = more power, easier coding

ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 928
As I've mentioned elsewhere, one of the cool aspects of configurations is that a custom feature can "call" a configured part studio.  This makes it easier to write custom features that create complicated geometry because you can model the complicated geometry interactively in a part studio.  However, the syntax to do this currently is somewhat arcane.  To make it easier, I've written an "instantiator" module that simplifies this and creates multiple instances of the same configuration efficiently.  My intent is to incorporate it into the FS standard library, but for now it's available as an Onshape document: https://cad.onshape.com/documents/333ec067d919f3b6987002ce/w/ffc55a1fc90a9ba29b73505e/e/18b028058ab32602ad4be712

Here's how the instantiator can be used:

firstPartStudio::import(...); // These imports are created using the import button in the Feature Studio

// later, in a feature
const instantiator = newInstantiator(id + "myId");

var firstQuery = addInstance(instantiator, firstPartStudio::build, { "configurationInput" : configurationValue }, transform(vector(1, 2, 3) * inch)));
var secondQuery = addInstance(instantiator, secondPartStudio::build, secondConfiguration, someOtherTransform));
// repeat the above as necessary

instantiate(context, instantiator); // This actually brings in the bodies
// Now firstQuery and secondQuery will resolve to the instantiated geometry

The document contains an example feature for adding keys to a keyboard that becomes much easier to create with the instantiator:

Please post feedback on the instantiator here!


P.S. The instantiator internally takes care of some efficiency concerns, deriving the first instance in a given configuration and body-patterning the rest.  User-overridable tolerances are used to determine which configurations are considered "the same."
Ilya Baran \ Director, Architecture and FeatureScript \ Onshape Inc


  • konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 911 ✭✭✭✭✭
    can this be used for substituting query parameters for a feature sequence from some source part studio, or this works only for numeric values?
  • mahirmahir Member, Developers Posts: 857 ✭✭✭✭
    @ilya_baran, just making sure I understand. Is the intention just to reduce the number of function calls when instantiating more than one configuration?

    In your previous example you were able instantiate a configuration with three commands - import(), importDerived(), and opTransform(). The resulting body can be patterned like any other body. For X configurations from Y part studios, you end up with Y+2X function calls.

    With the instantiator you combine the config input and transform, so 2X becomes X. You still need three function calls - partStudio::import(), addInstance(), and instantiate(), but instantiate() only has to be called once. You end up with Y+X+1 function calls (which is less than Y+2X when X>1).

    Is that the gist?
  • kevin_o_toole_1kevin_o_toole_1 Onshape Employees, Developers, HDM Posts: 449
    If I'm understanding you correctly, the answer is no. Derived brings in an entire part studio after all its features have regenerated. There's no way to bring in just some of its features. One useful invariant is that Derived Part Studio will always produce the same output for the same configuration, no matter where you use it.

    Am I understanding your question correctly? If not, could you clarify?

  • ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 928
    Reducing the number of function calls (and their ugliness -- the importDerived call is somewhat awkward) is part of the goal.  The bigger part is that opPattern is cheaper than importDerived and configurations are often repeated.  The instantiator only does importDerived for unique configurations and patterns the rest.
    Ilya Baran \ Director, Architecture and FeatureScript \ Onshape Inc
  • konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 911 ✭✭✭✭✭
    @kevin_o_toole_1 ok, thanks
    i hoped that since we can configure query fields in config table there may be a way to regenerate a sequence of features from that configured part studio into the target PS only changing those query parameters, like a function call with new argument.
  • mahirmahir Member, Developers Posts: 857 ✭✭✭✭
    edited January 2018
    @konstantin_shiriazdanov, I think only configured variables can be called externally. Regular configured feature inputs (numeric or otherwise) are not.
  • ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 928
    Ok, there is a bit of terminology confusion going on here.

    From a FeatureScript point of view, a Part Studio is a function that takes arguments (each argument is a "Configuration Input") that together make up the configuration.  These configuration inputs may be enums, booleans, strings, lengths, angles, or numbers.  They may not be queries.  "Configuration variables" is just a term for configuration inputs that are not enums or booleans.

    The part studio function creates a context, calls features (which are functions that take "Feature Parameters" as arguments), and returns the context.  Feature parameters can be lots of different types: every type allowed as a configuration input and also queries, lookup tables, feature lists, etc.

    The fact that you can configure queries means that that you can make a query feature parameter depend on the value of an enum or a boolean configuration input.  It does not mean that you can pass a query to a part studio (we did think about allowing this but it runs into serious difficulties I won't go into here) as a configuration input.

    Does this help?
    Ilya Baran \ Director, Architecture and FeatureScript \ Onshape Inc
  • konstantin_shiriazdanovkonstantin_shiriazdanov Member Posts: 911 ✭✭✭✭✭
    thanks, I think I understood the boundaries of the possible

  • Dylan_StewartDylan_Stewart Member, Developers Posts: 106 PRO
    edited January 2018
    @ilya_baran So is it now possible to configure part properties? 

    If so, how would you call that in FS? 
    Digital Engineering
  • ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 928
    edited January 2018
    @Dylan_Stewart not yet -- coming very soon.

    EDIT: to clarify, you can configure part properties interactively in the table.  What is coming soon is the ability to set part properties from FeatureScript.  When you bring in a part via derived (in FS or otherwise), its part properties come with it.  This does not yet work on the instantiator; that requires some internal changes.
    Ilya Baran \ Director, Architecture and FeatureScript \ Onshape Inc
  • Dylan_StewartDylan_Stewart Member, Developers Posts: 106 PRO
    @ilya_baran But if the part's properties are configured before exporting, the properties will follow when the part is imported?

    Digital Engineering
  • ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 928
    The instantiator has now been incorporated into the standard library: 


    It had some bugs fixed (bringing in parts with properties should work correctly now, mate connectors should work correctly now), improved performance, and some new capabilities, like using a mate connector in the part studio being imported as the "coordinate system", and controlling the identity of the instantiated parts using an existing entity (important in making robust references in later features to the instantiated parts).
    Ilya Baran \ Director, Architecture and FeatureScript \ Onshape Inc
  • burt_harrisburt_harris Member Posts: 3
    edited June 2018
    Thanks Ilya, I tried experimenting with the Keyboard feature, this seems to be quite close to what I need, documentation however needs some work. 

    For example, when I use Keyboard in a simple example, I get keys not only at the location in my sketch, but at the vertices of the underlying part I wanted to add a key to.    Perhaps the FeatureScript module I imported is out-of-date with the library implementation, could someone tell me what is going wrong.
  • kevin_o_toole_1kevin_o_toole_1 Onshape Employees, Developers, HDM Posts: 449
    When you create a sketch on a face, Onshape creates a sketch region with the boundary of that face. If you want to make a sketch without those boundary edges and vertices, you can create an offset plane zero inches from that face, and sketch on the offset plane.
  • burt_harrisburt_harris Member Posts: 3
    Got it, thanks.
  • klo_hoklo_ho Member Posts: 2 EDU

    @ilya_baran, thank you for creating and sharing with  “instantiator” and providing working code example. 

    I am a student at school 42(wiki link). My lab and I often need to design and print 3d parts. Our most common problem is that we often have to print out parts with slightly different dimensions because our 3d printer “overprints” and sometimes “underprints” depends on the thickness of the filament. 

    In order to figure out the right dimensions, I need to manually change the variables of the 3d model and save multiple  STLs and later combine them all in the slicer software. Very boring and manually intensive process :)

    Currently, we are trying to implement the same functionality but with the help of featurescript.

    Seems like configurations and “instantiator” are the right tool for this task.

    What do you think?

    Here are my questions:

    1: Can I access/create the configuration table from featurescript?

    Can I query configuration part for list of variables dynamically (with no GUI involved)?

    2: Can I “import” part into the script dynamically?

    Where can I find the path and version of parts in my current document via API?

    Thank you in advance.


  • kevin_o_toole_1kevin_o_toole_1 Onshape Employees, Developers, HDM Posts: 449
    Welcome, Klo!

    For your case, I'd recommend the following workflow:
    1) Model your part in a Part Studio in such a way that a single configuration variable input named e.g. "Offset distance" will control the offset distance. How this is modeled depends on the model itself. It could control the thickness of a thicken feature to offset surfaces, or some sketch dimension, whatever your current manual method is.

    2) In another Part Studio, insert a custom feature which takes as input a reference parameter to the original part.

    3) Either
    a) Give that feature an "isAnything(definition.offsetsToCreate)" parameter (which could take an expression like [ -0.01 * inch, 0 * inch, 0.01 * inch, 0.02 * inch ... ] or an expression like range(-0.1 * inch, 0.1 * inch, 10) ), or
    b) Define a standard set of offsets as an array in the feature code itself

    4) For each offset distance, call addInstance(instantiator, definition.myPartStudio, { configurationOverride: { "Offset_distance" : oneOffset })

    5) After calling instantiate(), you now have a Part Studio filled with all the parts you need to export. You might want to call setProperty(... { entities: addInstanceReturnValueGoesHere, propertyType: PropertyType.NAME }) at this point, to keep track of which part is which.

    6) Right click the Part Studio tab > export. Choose option to download all parts as separate STL files. File names will be the name of the top level Part Studio, followed by the name of the part (which you set in with setProperty)

    Hope this helps!
  • klo_hoklo_ho Member Posts: 2 EDU

    @kevin_o_toole_1 Thank you for your help!

    I am still confused though how to use function addInstance (instantiator is Instantiator, partStudio is PartStudioData, definition is map).

    Could you please provide small code snippet.

    How to use PartStudioData to specify user-selected parts?

    Can people select another part from previous PartStudio via precondition mechanism or only import function?

  • kevin_o_toole_1kevin_o_toole_1 Onshape Employees, Developers, HDM Posts: 449
    edited July 3
    Lots of info on reference parameters is in the imports documentation, and lots of examples are in the Reference parameter examples document linked there.

    Specifically, in the "place on lines" example, you'll see a helpful usage of addInstance() and surrounding code:
                addInstance(instantiator, definition.partStudio, {
                    "transform" : toWorld(cSys),
                    "configurationOverride" : {
                        "Length" : length

    Note that "Length" is the FeatureScript name of the relevant configuration input variable... Your input's name will be different. You can view that name in the Edit FeatureScript identifiers menu.

    Hope that helps!
  • MBartlett21MBartlett21 Member Posts: 1,615 EDU

    Could the instantiator have some performance improvements due to being able to query for specific instances of a pattern?
    MB - I make FeatureScripts: view FS (My FS's have "Official" beside them)
  • kevin_o_toole_1kevin_o_toole_1 Onshape Employees, Developers, HDM Posts: 449
    Our current assessment is that the speedup is not worth it. For cases where "identity" or "name" is provided for the instance, we do still want multiple pattern operations to preserve the identity of the created parts in downstream operations. For the "identity" case, imagine inserting items at several points in your model, then making downstream modifications to each instance, then removing one point. You want the remaining modifications to still affect the same instances, rather than jumping to some other instance at some other point.

    You're correct that we could get it down to one operation in the case that the "identity" or "name" arguments aren't provided. However, we did some performance testing when developing this feature and found that the overhead from multiple opPatterns vs. one large opPattern was minuscule. Moreover, there's still some identity concerns with that change, since existing usages of custom features might see downstream feature break when upgrading existing features to later versions.

    Of course, there are multiple versioning mechanisms that allow us to make changes like this without too much worry. Each usage of that custom feature is versioned to the end user, and the geometry which results from that feature is guaranteed to not change unless the end user manually updates. The standard library is also versioned, and each custom feature definition imports a fixed version. And even when manually bumping that std version, there are additional changes you could make to ensure your feature still maintains the old behavior! Still, there will always be reasons to bump a custom feature's version, and there will always be reasons to update std, and the work you need to do when updating std to keep things stable isn't obvious. Our current assessment is that the change here is not worth the tiny performance win.

    If we do a more explicitly-breaking change to the instantiator in the future, we'll definitely consider adding this change, but for now we think the least surprising behavior is to keep all current usages working nicely with downstream features even as you continue bumping your version of std.

    Sorry for the lengthy response, but there's definitely some nuance here and I wanted to give some insight into how we weigh the various considerations in continually improving our standard library!
Sign In or Register to comment.