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.

A Dome Feature

dave_cowdendave_cowden Member, Developers Posts: 470 ✭✭✭
Hello, everyone:

I have completed my first FS feature. It is very basic, but i learned a lot, and I think this may be helpful for others.

A 'Dome' amounts to a loft with a bunch of assumptions built in. The feature is available in Solidworks, but seems missing in OnShape.
It is useful as-is, but of course could be improved-- mainly by adding options for boundary conditions.

The full code is below.  It is pretty bad, because I was learning as I went.

I have also documented several improvements I would recommend, as a result of working through this example:

  1. Debugging is really, really lacking. It needs to be possible to include a message with each statement. Also, it is standard practice to include a debugging flag, so that it is easy to turn on/off all of the debug. I couldn't figure out how to do this. What i really want is to write a function like localDebug(message as string, object as Any ), and then do if ( DEBUG) debug(message, object)). This is not possible, as far as i can tell, because there isnt a variable type that accepts any variable type. ( according to the type documentation ) and, of course, there is no way to add a message in debug.  
  2. Documentation is lacking.  a new feature, for example, creates a 'context is Context' line, but the FS type documentation here (https://cad.onshape.com/FsDoc/values.html)  does not list this as a valid variable type. It leaves me wondering where the _real_ list of variable types is located.
  3. I thought the documentation for the opLoft function, and the code generated by the Part Studio would be the same, but they are not. It was very, very difficult to figure out what the parameters to the opLoft function were, when Part Studio produces a loft() function with a bunch of other map parameters
  4. I couldnt figure out how to create an enum type. I tried several things, and I think i got close, but with no examples and no docs, it was really hard
  5. I really wish there was a way to add helpful text to inputs. I think this is a feature request. Its hard to document your feature with current functionality.
  6. Several times i had syntax errors in the body of my script, but the PartStudio GUI incorrectly read "part precondition failed". The message confused me for a while till i figured out what was going on.
  7. As per another discussion, there is no centerOfMass function for a face.  This is needed for  simple Dome feature. I approximated with the parametric center, as was suggested when i asked about it earlier.
  8. I am confused about how to declare functions that take any type of object as an input. for example, how is the 2nd argument to the append function declared? Can i do that as well ? 

FeatureScript 275;
import(path : "onshape/std/geometry.fs", version : "275.0");
annotation { "Feature Type Name" : "Dome" }
// Creates a Simple Dome from a face. 
// It amounts to a simplified loft, but it is sometimes nice not to have to do all 
// of the hard work yourself.
//
// The top of the dome will be placed along a vector normal to the selected face,
// at the parametric center of the face. I would prefer to use the center of mass,
// but this is not possible in feature script.
// future versions would primarily focus on lots of other options for the beginning 
// and ending boundary conditions
//
// Copyright dcowden, 1/16/2016





export const resultDome = defineFeature(function(context is Context, id is Id, definition is map)
    
    precondition
    {
        // The height of the dome
        annotation { "Name" : "Dome Height" }
        isLength(definition.height, LENGTH_BOUNDS);
        
        
        //the face on which to create a dome
        //must not consist of multiple edges.
        //NOTE: i could not find a way to compute the 'outer edge' of a face, that's what i would do
        annotation { "Name" : "Face To Dome", "Filter" : EntityType.FACE, "MaxNumberOfPicks" : 1 }
        definition.face is Query;
        
        
        //should we match starting curvature?
        annotation { "Name" : "Match Starting Curvature" }
        definition.matchStartCurvature is boolean;                
        //should we match a tangent plane at the end
        annotation { "Name" : "Tangent At End" }
        definition.tangentAtEnd is boolean; 
    }
    {
        //this is horrible.
        //i really want obj is Object or Any or something!
        //so basically this doesnt work!
        //function localDebug(context is Context, message is string, obj is Any ){
        //    if ( DEBUG ){
        //        debug(context, message, obj );
        //    }
        //}
        //but that doesnt seem possible because there is no 'Any or object' type, that accepts any argument.
        //plus, i dont know how to define a function parameter that accepts a context object, since it is not a valid type.
        const DEBUG = false;
        
        if ( DEBUG ){      
            debug(context,"Test");
            debug (context,  definition.face );
        }
        
        //find the edges of the Face
        var edgesOnFace = qEdgeAdjacent  (definition.face, EntityType.EDGE );
        
        if ( DEBUG ){
            debug (context, edgesOnFace );
        }
        
        var edges = evaluateQuery(context, qEntityFilter(definition.face, EntityType.EDGE));
        if (size(edges) > 1)
        {
            throw "Cannot Create a Dome on a face with more than one Edge, yet";
        }
        //evaluate a tangent plane at the parametric center of the face
        var centerPlane = evFaceTangentPlane(context, {
                "face" : definition.face,
                "parameter" : vector(0.5, 0.5)
            });

        //find the vector normal to the face at the center point of the Face
        var origin = centerPlane.origin;
        
        if ( DEBUG ){
            debug(context,origin);
        }
        
        var normal = centerPlane.normal; //vector
        
        if ( DEBUG ){
            debug(context,normal);
        }
        
        //create a point in space
        var topOfDome = (normal * definition.height)  + origin ;
        
        if ( DEBUG ){
            debug(context,topOfDome);
        }
        
        const vertexID = id + "topVertex";
        opPoint(context, vertexID, { point : topOfDome });
        var topVertex = qCreatedBy(vertexID, EntityType.VERTEX);
        
        if ( DEBUG ){
            debug(context, topVertex );
            debug(context, evVertexPoint(context, {
                   "vertex" : topVertex
            }));        
        }
        
        var profiles = [definition.face, topVertex];
        //create start condition and end conditions
        var dInfo = [];
        
        var startConditionMap = { };
        if ( definition.matchStartCurvature  ){
            var adjacentFaces = qEdgeAdjacent(definition.face, EntityType.FACE);
            startConditionMap.profileIndex = 0;
            startConditionMap.matchCurvature= true;
            startConditionMap.adjacentFaces =  adjacentFaces;
            dInfo = append(dInfo, startConditionMap);
        }
        if ( definition.tangentAtEnd) {
            dInfo = append(dInfo, { profileIndex: 1, "vector": normal , "magnitude": 1.0, "tangentToPlane": true  } ); //end constraint: the vertex point    
        }
        
        
        //create a surface that represents the Dome
        opLoft(context, id + "loft", { profileSubqueries : profiles, 
            bodyType : ToolBodyType.SOLID, 
            operationType : NewBodyOperationType.ADD,
            derivativeInfo: dInfo
        });
        
    }, {  });


Comments

  • dave_cowdendave_cowden Member, Developers Posts: 470 ✭✭✭
    additionally I have shared a document that defines and demonstrates the feature with OnShape support. It can be found here:
    https://cad.onshape.com/documents/4789a5ead96249ca887238e9/w/f87b9ae18ede4764b47922af/e/e1ea8b3c80e844eabb01ae80

    Is there a better way to share it than that right now?
  • ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 1,173
    edited January 2016
    Very cool!  Yes, dome is currently missing and this is a simple, but useful workaround.  I'm pretty impressed you were able to use opLoft -- that's the most complicated operation.  Your feedback is also very useful to us :smile: 
    A few responses to your comments:

    1. Declaring the type of variable in a function is optional -- if you don't specify a type, you can pass in anything.  So you could do this in your feature:
    const localDebug = function(message is string, object)
    {
        if (DEBUG) // Or use a part studio variable and get it from the context here
        {
            print(message ~ ": ");
            debug(context, object);
        }
    };
    
    2. FeatureScript has a fixed set of "standard types", which are built into the language itself and are the ones enumerated and a possibly infinite set of enum types and "custom types" which are defined all over the standard library and you can define new ones.  Context is one of these custom types.  The underlying standard type for a Context is "builtin".  Yes, this is more confusing than I would like.

    3. Yes, this is the distinction between loft, the feature, and opLoft, the lower-level operation that does much of the work.

    4. There examples all over the standard library but this defines an enum type:
    export enum MyEnum { ONE, TWO, THREE }
    
    5. That's a very good point.  We have ideas for making the parameter name more visible, but being able to add more helpful in-context documentation seems like an important capability.

    6. Not sure what was going on there -- you can use the feedback tool to illustrate a specific case like this.

    7. Agreed -- we'll have more mass properties / center of mass fairly soon.

       -Ilya
    Ilya Baran \ VP, Architecture and FeatureScript \ Onshape Inc
  • dave_cowdendave_cowden Member, Developers Posts: 470 ✭✭✭
    excellent! i'll modify my example to do as you recommended with debug: i suspect it will be a very common pattern ( and perhaps a best practice, so its easy to run a feature in debug ).
  • dave_cowdendave_cowden Member, Developers Posts: 470 ✭✭✭
    Oh, one other thing I couldnt figure out how to do is to compute the 'outermost' edges for a Face. If I had that, i could elminate problems when people try to do a dome on a face with internal loops.
  • ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 1,173
    Regarding the sharing, unfortunately until we make FeatureScript public, documents with FeatureScript can't be public.  Posting source to the forum is probably the best way, for now.  I can view documents shared with support, but the other FeatureScript developers cannot (even once you share with support we try to limit the exposure of user data within Onshape).
    Ilya Baran \ VP, Architecture and FeatureScript \ Onshape Inc
  • dave_cowdendave_cowden Member, Developers Posts: 470 ✭✭✭
    alright thanks. i did the right thing then sharing with you. I saw you briefly in there looking.

    I've started creating my first feature designed to solve a real problem-- and unfortunately i'm flailing about like a total noob , so i've posted some more questions... But the source code does really help, thanks for that.
Sign In or Register to comment.