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.
FeatureScript: Evaluate generic expression
mahir
Member, Developers Posts: 1,307 ✭✭✭✭✭
I'm currently working on an equation driven 3D curve, but I'm having trouble getting FeatureScript to evaluate string expressions as values. For example, if #T=45, then setVariable(context, "X", "tan(#T)") should set #X to 1. Instead, it just gets stored as a string. Any ideas?
0
Comments
Unfortunately I think having the feature user input the equation is very difficult to do for now, since we don't allow much even in the isAnything input and FS isn't great for string parsing. We will need to do a bit of work on our side to make this possible...
Declare an Expression type and a constant t_ that represents a free variable. Overload operators for Expression type to construct expression trees and write an evaluate function. Overload functions like sin, cos, etc. to also take an Expression. Probably ignore units for the time being.
Make an initial feature that sets the context variable #t_ to t_ (or zero if the "entry" box is checked -- that way the user will be able to enter an expression in terms of #t_ in your curve feature).
In your curve feature, get an isAnything and expect a number (if the "entry" box is checked) or an Expression (if it is not). If it's an Expression, evaluate it for t_ between 0 and 1 to get the curve values.
This is a fair bit of work, but less than a parser I think. Let me know if I can clarify anything...
https://cad.onshape.com/documents/9b37f9cdfae994fddb9008cf/w/292c115208353529b90cc5f4/e/847cea1a0210d7ba3dbfa7ed
Assuming I can get it working, I have a couple other questions. Can this be done without a "pre-feature" designating the free parameter? Also, do I have to export constants as well (like PI)? Also, I'd like to be able to specify #X, #Y, or #Z as a function of the other variables. Aside from checking for circular references, e.g. X(Y) & Y(X), how would I check when one XYZ definition references another XYZ definition? This would be necessary to ensure calculation order - X(T) before Y(X,T) before Z(X,Y,T). Would I need to declare 3 more constants x_, y_, and z_?
tl;dr:
Ilya's utilizing the expression parsing that already exists in FeatureScript, but instead of creating a value (as the math usually does) it creates a function that returns a value when passed free parameter t.
Long:
In FeatureScript, you create a lambda function and pass it around like any other variable. (FS documentation) (more on lambdas). Such functions are not evaluated when they are defined – instead, they are evaluated later, when provided input parameters.
In FeatureScript, you can also define operator overloads for any type, which behave differently than the standard operation. (FS documentation).
Given this, Ilya's has defined a specific type called "Expression". An Expression, rather than having a single value, is a function which can return a value when passed a parameter (t). The constant t_ is a trivial Expression which returns itself (made accessible as #t via the first feature). So, when a user types #t into the "x" field in the a dialog, definition.x is set to an Expression into which you can pass any value and get that value back.
Now, where this gets useful is that Ilya also defined several overloads for common operators on Expressions (and, through the magic of FeatureScript, these overloads are visible and can be used inside the Part Studio). So, for instance, when you add a number to an Expression, rather than getting a result, you get another Expression which can return a result once you pass in the variable t. When a user types #t + 1 into the dialog, the result is a function which takes any value, t, and gives back t + 1.
The overall effect is that, when you type a full expression in this dialog, all of its operators are evaluated in FeatureScript, and those that involve #t will result in full Expressions. This mechanism will be compatible with whatever functions or operators you overload for the Expression type.
Hope that helps.
On your questions:
This is not how we want to be doing this long-term in FeatureScript. It doesn't extend nicely to multiple variables, it requires a special feature to define the free parameter, its not clear to the user which variables drive the curve, and which are regular FS variables, etc. It's an elegant hack, but a hack nonetheless.
If you want X, Y, or Z to be functions of each other, you can set a variable feature #X = blah * #t, then in another variable feature say #Y = blah * #X * #t, then in the curve feature set x to #X, etc. I don't think there's any way to set them all to be functions of each other in one feature, since all feature inputs are evaluated when the feature is created, before you have a chance to hook them up (a useful property, since it prevents circular references like you describe).
- Unsuppress the variable #t = 0.5
- Edit the feature to the equations you want
- Suppress the variable #t = 0.5
The reason you have to do this is that there's currently validation in our dialogs that make sure the expression a user types evaluates to a number or value with units. Our new Expression type is neither (it's a function), so you need the fake variable to get it to evaluate to something valid during the time when this is checked.Like I said, the UX is not good right now, since our dialogs were never designed for this purpose. The fact that a solution like this exists is a side effect of having the power of a full programming language in your CAD system, but ultimately, you are going to have to wait for us to build this kind of thing into the product to get the right workflow.
FeatureScript Parametric Curve
@kevin_o_toole_1
Here is a library that I did to parse an expression and evaluate it.
To evaluate a string, call evalExpression with the string and a map of variables (the map of variables can generally be constructed with mergeMaps(getAllVariables(context), {"variable" : "value"}) to get variables from the context)
The function evalExpression returns a map with fields "type" and "value"
If the type == "empty" then there is no expression to evaluate.
If the type == "depends" then there is some variables that it needs to know to evaluate the expression. The value is then an array of variable names.
If the type == "result" then the value is the result.
http://onsha.pe/documents/429fd7dda5f0fdb93833a34a
IR for AS/NZS 1100