Welcome to the Onshape forum! Ask questions and join in the discussions about everything Onshape.
First time visiting? Here are some places to start:- Looking for a certain topic? Check out the categories filter or use Search (upper right).
- Need support? Ask a question to our Community Support category.
- Please submit support tickets for bugs but you can request improvements in the Product Feedback category.
- 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_cowden
Member, Developers Posts: 475 ✭✭✭
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:
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:
- 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.
- 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.
- 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
- 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
- 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.
- 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.
- 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.
- 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
});
}, { });
1
Comments
https://cad.onshape.com/documents/4789a5ead96249ca887238e9/w/f87b9ae18ede4764b47922af/e/e1ea8b3c80e844eabb01ae80
Is there a better way to share it than that right now?
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:
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:
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
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.