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.

Joining Tabs/Tags on Frames

aaron_harris672aaron_harris672 Member Posts: 9 PRO

Hi,

Can anyone help me with trying to set up a feature script so that I can easily add in joining tab/tags to frames using either box section or angle. i have tried with the following code but keep getting errors.

FeatureScript 1640; // Specify the FeatureScript version

// Feature definition
annotation { "Feature Type Name": "Joining Tab" }
export const joiningTab = defineFeature(function(context is Context, id is Id, definition is map)
precondition
{
// Tab width as a length input
annotation { "Name": "Tab Width", "UIHint": "DISTANCE" }
definition.tabWidth is length;

    // Tab height as a length input
    annotation { "Name": "Tab Height", "UIHint": "DISTANCE" }
    definition.tabHeight is length;

    // Tab spacing as a length input
    annotation { "Name": "Tab Spacing", "UIHint": "DISTANCE" }
    definition.tabSpacing is length;

    // Joining part input (should be a body or face)
    annotation { "Name": "Joining Part" }
    definition.joiningPart is Query;
}
{
    // Fetch the selected edges for tab creation
    const edges = evaluateQuery(context, definition.edges);

    // Loop through the edges and create tabs
    for (var edge in edges)
    {
        // Extract the tangent line from the edge
        const edgeCurve = evEdgeTangentLine(context, { "edge": edge });
        const edgeLength = evLength(context, { "entities": edge });

        // Create tabs along the edge at intervals
        for (var pos = 0; pos < edgeLength; pos += definition.tabSpacing)
        {
            const tabOrigin = edgeCurve.origin + edgeCurve.direction * pos;

            // Define the tab plane for extrusion
            const tabPlane = plane(tabOrigin, edgeCurve.direction, vector(0, 0, 1));

            // Create the tab's sketch
            const tabSketch = planarSketch(context, id + "tabSketch" ~ pos, {
                "plane": tabPlane
            }, function(sketchContext) {
                // Draw a rectangle to represent the tab
                skRectangle(sketchContext, "tabRect", {
                    "corner1": vector(0, 0) - vector(definition.tabWidth / 2, 0),
                    "corner2": vector(0, definition.tabHeight) + vector(definition.tabWidth / 2, 0)
                });
            });

            // Extrude the tab from the sketch
            const tabExtrude = opExtrude(context, id + "tabExtrude" ~ pos, {
                "entities": qSketchRegion(id + "tabSketch" ~ pos),
                "direction": edgeCurve.direction,
                "operationType": NewBodyOperationType.ADD,
                "depth": definition.tabHeight
            });

            // Subtract the tab from the joining part
            opBoolean(context, id + "tabCutout" ~ pos, {
                "tools": qCreatedBy(tabExtrude, EntityType.BODY),
                "targets": definition.joiningPart,
                "operationType": BooleanOperationType.SUBTRACT
            });
        }
    }
}

);

Comments

  • jnewth_onshapejnewth_onshape Member, Onshape Employees Posts: 89

    Share doc and post the errors. I'll take a look.

  • aaron_harris672aaron_harris672 Member Posts: 9 PRO

    Hi Thanks for reaching out I am very new to feature scripts link to document below.

    https://cad.onshape.com/documents/d3dd2419cf94e88c143b6d31/w/1ac9b163e394c31738980de7/e/869503fbf124c58f6215a02e

    Errors

    unction defineFeature with 1 argument(s) not found

    5:27   

    Feature Studio Joining Tab

    No declaration found for type Context.

    5:61   

    Feature Studio Joining Tab

    No declaration found for type Id.

    5:76   

    Feature Studio Joining Tab

    Nonconforming feature function 'joiningTab': precondition analysis failed

    5:14   

    Feature Studio Joining Tab

    No declaration found for type length.

    10:32   

    Feature Studio Joining Tab

    definition.tabWidth: Unrecognized type

    10:32   

    Feature Studio Joining Tab

    No declaration found for type length.

    14:33   

    Feature Studio Joining Tab

    definition.tabHeight: Unrecognized type

    14:33   

    Feature Studio Joining Tab

    No declaration found for type length.

    18:34   

    Feature Studio Joining Tab

    definition.tabSpacing: Unrecognized type

    18:34   

    Feature Studio Joining Tab

    definition.joiningPart: Missing filter and no ALWAYS_HIDDEN UI hint

    No declaration found for type Query.

    Function evaluateQuery with 2 argument(s) not found

    Function evEdgeTangentLine with 2 argument(s) not found

  • aaron_harris672aaron_harris672 Member Posts: 9 PRO

    This is what I would like to be able to achieve after a frame has been created.

  • wayne_sauderwayne_sauder Member, csevp Posts: 555 PRO

    I would love to see the script if you can get this working. I often thought about it but never pursued it.

  • jnewth_onshapejnewth_onshape Member, Onshape Employees Posts: 89

    @aaron_harris672 No worries on starting out. Everyone starts from somewhere. FS can be rough to get started with. I read through your doc. Here are the basics: try to figure these out and fix the mistakes and then post again. Also share by clicking "Share" and making the doc public, then posting the url. The link share stuff is not that useful. Try the #1-#4 posted below and see what else shakes out:

    FeatureScript 2506;
    
    //#1: Need to import this to define all those boilerplate functions like `defineFeature`
    import(path : "onshape/std/common.fs", version : "2506.0");
    
    //#2: Nonconforming feature definition, precondition failed. thats because:
    // Feature definition
    annotation { "Feature Type Name": "Joining Tab" }
    export const joiningTab = defineFeature(function(context is Context, id is Id, definition is map)
        precondition
        {
            //#3
            // you write "definition.tabWidth is length;"
            // but that's not how you define a lengthParameter.
            // This is how you write a length parameter:
            // annotation { "Name" : "My Length" }
            //isLength(definition.myLength, LENGTH_BOUNDS);
            //Above, next to "New feature" you ll see a "p hamburger menu v".
            //Click it, then click "Length parameter". It will autocreate the correct syntax.
    
            // // Tab width as a length input
            // annotation { "Name": "Tab Width", "UIHint": "DISTANCE" }
            // definition.tabWidth is length;
    
        
            // Tab height as a length input
            // annotation { "Name": "Tab Height", "UIHint": "DISTANCE" }
            // definition.tabHeight is length;
    
            // // Tab spacing as a length input
            // annotation { "Name": "Tab Spacing", "UIHint": "DISTANCE" }
            // definition.tabSpacing is length;
    
            // // Joining part input (should be a body or face)
            // annotation { "Name": "Joining Part" }
            // definition.joiningPart is Query;
        }
        {
            // Fetch the selected edges for tab creation
            const edges = evaluateQuery(context, definition.edges);
    
            // Loop through the edges and create tabs
            for (var edge in edges)
            {
                // Extract the tangent line from the edge
                const edgeCurve = evEdgeTangentLine(context, { "edge": edge });
                const edgeLength = evLength(context, { "entities": edge });
    
                // Create tabs along the edge at intervals
                for (var pos = 0; pos < edgeLength; pos += definition.tabSpacing)
                {
                    const tabOrigin = edgeCurve.origin + edgeCurve.direction * pos;
    
                    // Define the tab plane for extrusion
                    const tabPlane = plane(tabOrigin, edgeCurve.direction, vector(0, 0, 1));
    
                    // Create the tab's sketch
                    // #4:
                    //this is not how you create a planar sketch. Did you define this function somewhere? It is not in this file.
                    //what you want is more like:
                    // newSketchOnPlane(context, id + "sketch1", {
                    //         "sketchPlane" : plane(vector(0, 0, 0) * inch, vector(0, 0, 1))
                    // });
                    // skRectangle(...);
                    // skSolve(...);
                    
                    
                    const tabSketch = planarSketch(context, id + "tabSketch" ~ pos, {
                        "plane": tabPlane
                    }, function(sketchContext) {
                        // Draw a rectangle to represent the tab
                        skRectangle(sketchContext, "tabRect", {
                            "corner1": vector(0, 0) - vector(definition.tabWidth / 2, 0),
                            "corner2": vector(0, definition.tabHeight) + vector(definition.tabWidth / 2, 0)
                        });
                    });
    
                    // Extrude the tab from the sketch
                    const tabExtrude = opExtrude(context, id + "tabExtrude" ~ pos, {
                        "entities": qSketchRegion(id + "tabSketch" ~ pos),
                        "direction": edgeCurve.direction,
                        "operationType": NewBodyOperationType.ADD,
                        "depth": definition.tabHeight
                    });
    
                    //#5 BooleanOperationType.SUBTRACT is not a valid enum (read the error message or hover over the yellow highlighting)
                    // If you write `BooleanOperationType.` then the system will come back with a list of valid completions. You probably meant
                    //BooleanOperationType.SUBTRACTION
                    
                    // Subtract the tab from the joining part
                    opBoolean(context, id + "tabCutout" ~ pos, {
                        "tools": qCreatedBy(tabExtrude, EntityType.BODY),
                        "targets": definition.joiningPart,
                        "operationType": BooleanOperationType.SUBTRACT
                    });
                }
            }
        }
    );
    
    

  • aaron_harris672aaron_harris672 Member Posts: 9 PRO

    Thanks @jnewth_onshape for reaching out. I have updated the lines you pointed out and I have shared the document. I am still getting a couple of errors. See below.

    https://cad.onshape.com/documents/d3dd2419cf94e88c143b6d31/w/1ac9b163e394c31738980de7/e/869503fbf124c58f6215a02e

     Feature Studio Joining Tab

    Missing FeatureScript version

       

    Feature Studio Joining Tab

    Cannot import a module with FeatureScript version: 2506 from module with older version 0

       

    Feature Studio Joining Tab

  • jnewth_onshapejnewth_onshape Member, Onshape Employees Posts: 89
    edited November 25

    Good progress. Here's what's happening:

    The first is the FeatureScript version declaration at the top of the shared FS. You need something like:

    //JMN: You need this directive at the top of a FS doc.
    FeatureScript 2522;
    import(path : "onshape/std/common.fs", version : "2522.0");
    

    (That first line isn't present in your shared doc).

    Second, your UI parameter declaration in the precondition is not valid FS. I'm wondering where you got this code from. If ChatGPT/Claude: I applaud your efforts to use AI (I have been experimenting with it myself on side projects), but caution you not to use it for FS. The training set is likely too small for such an approach.

    Let's try one of your length parameters. These two lines are incorrect:

        annotation { "Name": "Tab Width", "UIHint": "DISTANCE" }
        definition.tabWidth is length;
    

    "UIHint": "DISTANCE" is wrong because "DISTANCE" is not a valid UIHint. You can see which are valid by typing "UIHint." (the period is important) and then waiting a moment. Code completion will run and suggest the set of valid completions. I'd recommend steering clear of UIHints till you have a better grasp of FeatureScript. They're useful but not necessary for you (yet).

    The second part is definition.tabWidth is length; This is almost valid FS but not in a precondition. The way you want to declare a parameter is:

        annotation { "Name" : "Width" }        
        isLength(definition.tabWidth, LENGTH_BOUNDS);
    

    This creates a field called "Width" in your UI that will take values with length units (m, cm, in, etc). Later, when you are writing your FS code to use this width you can get the value using definition.tabWidth

    The last part I can see for now is, once you've fixed the above, the failures disappear. Then, if we attempt to use this FS in a part studio, it fails with:

    At line 34 we see the line:

    const edges = evaluateQuery(context, definition.edges);
    

    Which is telling you that you have passed a parameter of type Context (the context) and a parameter of type undefined. This makes sense - nowhere in your UI do you declare a parameter called definition.edges

    Here's a shared doc where you can see the above fixes:


    https://cad.onshape.com/documents/e35df38b439041e2472a8cd3/w/85d1a5a847931b68259679ee/e/ea91e81c9302d4bfc19164ea

  • aaron_harris672aaron_harris672 Member Posts: 9 PRO

    Hi @jnewth_onshape Your right I did try and use Chat GPT to help me generate the feature script. As I say I have never done this before and wasn't sure where to start. Best way I find to learn is by giving it ago and trying to work out what's not working. Hopefully I can learn it and start to understand it in time.

    I have updated my Feature Script code and added in the lines for the Width, Height, Spacing & Joining part. I am unsure what I need to put for line 36.

    I am also getting some new errors.

      Feature Studio Joining Tab

    Error in initializer function arguments

    7:40   

    Feature Studio Joining Tab

    no viable alternative at input 'isLength(definition.JoiningPart, is'

    36:42   

    Feature Studio Joining Tab

    extraneous input ')' expecting {<EOF>, 'annotation', 'export', 'enum', 'type', 'function', 'predicate', 'operator', 'typeconvert', 'const', TOP_SEMI}

    82:1   

    Feature Studio Joining Tab

    https://cad.onshape.com/documents/d3dd2419cf94e88c143b6d31/w/1ac9b163e394c31738980de7/e/869503fbf124c58f6215a02e

  • jnewth_onshapejnewth_onshape Member, Onshape Employees Posts: 89

    I would agree - best way to learn is to try to build something you want, then figure out what you need to know to make it happen.

    I followed that link you posted but do not see those errors. Are you sure you posted the latest and greatest?

  • aaron_harris672aaron_harris672 Member Posts: 9 PRO

    @jnewth_onshape I have just versioned it (V5). Try the link below.

    https://cad.onshape.com/documents/d3dd2419cf94e88c143b6d31/w/1ac9b163e394c31738980de7/e/869503fbf124c58f6215a02e

  • jnewth_onshapejnewth_onshape Member, Onshape Employees Posts: 89

    I'm not seeing those errors when I look in your document. Here's what I see in the FeatureScript notices area when I look at the FeatureScript tab:

    Clean as a whistle. This means there are no 'grammatical' errors in your FS. So far so good.

    If I attempt to use that feature in a Part Studio, I see:

    So that tells me there is something wrong with line 35 of "Joining Tab". If I go to that line I see:

        const edges = evaluateQuery(context, definition.edges);
    

    You have not defined a parameter definition.edges in your FeatureScript parameter list. Here's what I would recommend. Go to the precondition (where you define the definition.width parameter and do this:


    Selecting "Query parameter" will add some boilerplate code to your precondition. I modified that boilerplate to say:

        annotation { "Name" : "Edges", "Filter" : EntityType.EDGE, "MaxNumberOfPicks" : 1 }
        definition.edges is Query;
    

    So theres a lot in here. I specify a filter, to get only edges. I also limit the number of selections to 1 (do this at first, then remove this "MaxNumber" parameter when you are ready to modify the code to work on more).

    And now we have definition.edges which can be evaluated.

    Now this gets further:

        const edges = evaluateQuery(context, definition.edges);
    
        // Loop through the edges and create tabs
        for (var edge in edges)
        {
            // Extract the tangent line from the edge
            const edgeCurve = evEdgeTangentLine(context, {
                    "edge" : edge, //qNthElement(qEverything(EntityType.EDGE), 1),
                    "parameter" : 0.5 //parameter
            });
            //const edgeCurve = evEdgeTangentLine(context, { "edge" : edge });
            const edgeLength = evLength(context, { "entities" : edge });
    

    Your call to evEdgeTangentLine was incorrect but the above version works. The way you can get it right is by writing evEdgeTangentLine and then pausing a moment. A popup will appear showing you its parameters. To evaluate an edge to get a line (which is a straight line with an origin), you need to tell it where on the curve to evaluate. 0 is the beginning, 1 is the end.

    Ok so I think now you know:
    1. how to create parameters

    2. how to evaluate edge queries

    3. how to look at FeatureScript notice area.

    As soon as you run the above, you'll see new errors:

    But here's where the real work begins. I can definitely fix this problem - but so can you. This is something I learned during my compilers course back in school: the compiler (or in this case, the parser) is trying to help you! So read that message. What does it mean? It means on line 57, theres a function defineFeature. Inside it there's a call at 54 to vector.fs which is involved with adding things. The problem is that you have a ValueWithUnits (which is an object, a map), and a number, and the system doesnt know how to add those two different objects together. If I look at the line:

    const tabOrigin = edgeCurve.origin + edgeCurve.direction * pos;

    What are the two different objects? You have an edgeCurve.origin - what is that? It's a vector. That's why the vector.fs is being called, to add itself to edgeCurve.direction*pos. So what are those things? What is direction? Where is it coming from? Can you check its type? What about pos? What value is it?

    A useful way to get some idea about what those values are is just to print them to the FeatureScript notice area:

    println("here is the origin: " ~ edgeCurve.origin);

    That line will take "here is the origin: " and will concatenate whatever edgeCurve.origin is, and then post it to the notice area. Here's what I added:

           for (var pos = 0; pos < edgeLength; pos += definition.tabSpacing)
            {
                println("pos: " ~ pos);
                println("definition.tabSpacing: " ~ definition.tabSpacing);
                print("edgeCurve.origin: " ~ edgeCurve.origin);
                println("edgeCurve.direction: " ~ edgeCurve.direction);
                
                const tabOrigin = edgeCurve.origin + edgeCurve.direction * pos;
    

    And here is the result:

    So that pos is just a number. Probably not what you intended.

    The thing you're trying to add to pos, tabSpacing, is not defined. Do you need a parameter for this?

    The origin is a vector as shown, and you can see that it's a vector filled with "ValueWithUnits" (thats how FeatureScript represents quantities, like "
    one inch" instead of just numbers like "one").

    So take a look at this and I would recommend removing everything downstream and try to just have it produce values you want: positions, directions, origins, etc.

    At this point you probably need to learn the basics of FeatureScript. Our very own Lindsay Early has come up with some fantastic learning pathways for FeatureScript. Go here and grind through this and then come back to the forums:

    https://learn.onshape.com/courses/featurescript-fundamentals

    It might take you a couple hours, but man our educational content is fantastic. I've done a fair few of these myself because there is always something new to learn.

Sign In or Register to comment.