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 number of issues with equation support
neobobkrause
Member Posts: 105 EDU
The OnShape equation solver seems unnecessarily restrictive and fragile. I'm finding myself spending an inordinate amount of my design time trying to understand why an expression is being flagged as invalid and trying through trial and error to find workarounds.
Here are some examples. I'll have to show errors as struck-out text as the html editor doesn't allow me to color text.
Variable #a = 2 mm
Variable #b = #a*3
I know that the documentation gives some explanation for why this is an error, but it shouldn't be. It's unambiguous. If the equation grammer does in fact render this ambiguous, then I suggest that perhaps the grammer definition should be re-examined.
Variable #a = 2 mm
Variable #b = 3 mm
Variable #c = #a/#b
I understand the rational given for why this is flagged as invalid. Yet a designer might have a legitimate need for something like this when, for example, calculating a diagonal. I would even suggest that the solver accept a units divisor when the units are different than the numerator.
A related suggestion: The equation parser should minimally include a value() function that strips away the units of the given expression.
I think the size of the textedit control that an expression is entered in when setting dimensions or other contraints needs to be appropriately large given the length of the expression. I sometimes end up needing to paste an expression into the windows of some other application so that I can see the full expression as it's being constructed and edited.
I also think an expression editor control that includes syntax coloring and substring completion would add immensely to the UX.
I can appreciate OnShape's variable support is a fairly new feature. As such, I'm encountering a fair number of idiosyncratic quirks that remain that I end up investing a non-trivial amount of time on trying to determine whether the error is in my expression or an OnShape quirk. User productivity will improve as the grammer matures and these quirks are identified and resolved.
- Bob
Here are some examples. I'll have to show errors as struck-out text as the html editor doesn't allow me to color text.
Variable #a = 2 mm
Variable #b = #a*3
I know that the documentation gives some explanation for why this is an error, but it shouldn't be. It's unambiguous. If the equation grammer does in fact render this ambiguous, then I suggest that perhaps the grammer definition should be re-examined.
Variable #a = 2 mm
Variable #b = 3 mm
Variable #c = #a/#b
I understand the rational given for why this is flagged as invalid. Yet a designer might have a legitimate need for something like this when, for example, calculating a diagonal. I would even suggest that the solver accept a units divisor when the units are different than the numerator.
A related suggestion: The equation parser should minimally include a value() function that strips away the units of the given expression.
I think the size of the textedit control that an expression is entered in when setting dimensions or other contraints needs to be appropriately large given the length of the expression. I sometimes end up needing to paste an expression into the windows of some other application so that I can see the full expression as it's being constructed and edited.
I also think an expression editor control that includes syntax coloring and substring completion would add immensely to the UX.
I can appreciate OnShape's variable support is a fairly new feature. As such, I'm encountering a fair number of idiosyncratic quirks that remain that I end up investing a non-trivial amount of time on trying to determine whether the error is in my expression or an OnShape quirk. User productivity will improve as the grammer matures and these quirks are identified and resolved.
- Bob
0
Comments
What am I missing? Except that in the second example, you want #a/#b instead of a/b.
As I say, it's sometimes difficult to determine why an expression throws an error. I have a drawing that includes two variables, let's call them #BaseHeight and and #RimHeight, that I want to define like this...
Variable #BaseHeight = .2 mm
Variable #RimHeight = ceil(#BaseHeight*3)
As noted from my poor-man's styling, the second definition throws an error for some reason I've been unable to determine.
Any suggestions are appreciated.
- Bob
The expression above won't work because the ceil function won't take a length. The reason it won't is that depending on the units that the length is expressed in, the result will be different: what you'd expect from ceil(2.54 cm) will be different from ceil(1 in) even though the two lengths are the same. And it's not clear at all what to do with ceil(1 in + 3 m^2 / (2 cm)). For the same reason, the value function you propose would not work. I would argue that a system where if I enter the same length in different units and the behavior downstream changes has a serious problem.
The way to do what I think you want to do here is to explicitly specify which units you want to use ceil in as follows:
Variable #RimHeight = ceil(#BaseHeight*3 / mm) mm
Solutions aren't always easy or obvious. And I'm probably not the best person to suggest a solution that works syntacticly, semantically and UX-ally. But let me ask some questions if you'll allow me.
Suppose I define a variable #a. Further suppose that I reference this variable in an equation elsewhere in my document. If I later change the name of #a to #b, it appears that equations containing the variable reference doesn't get automatically updated to reflect the new name. Rather, the designer must manually change the text of every equation with references.
It's been my experience that it's important that development environments support refactoring scenarios like this.
- Bob
Here are loosely organized thoughts in the meantime. The way to think about units in Onshape is that when you have an expression like "2.5 cm", you have an actual length -- the length itself doesn't know what units it's expressed in -- "2.5 cm" can be interchanged with "25 mm" in all situations and will work absolutely identically. I believe that this is a critical property of a well-functioning system.
Let's break down the expression I wrote, "1 in + 3 m^2 / (2 cm)". "1 in" is a length. "3 m^2" is an area. "2 cm" is a length. "3 m^2 / (2 cm)" is an area divided by a length, hence a length. The whole expression is therefore two lengths added together and therefore evaluates to a length.
Determinism is not an issue here -- when you enter a length, we deliberately immediately forget the units in which the length was entered. To put it another way, w.r.t. "ceil", what is your height rounded up? That question doesn't make sense without also saying something like "to the next centimeter".
To explain my workaround, "#BaseHeight*3/mm" is a length times a unitless quantity (3) divided by a length (mm), hence it's a unitless quantity that ceil can process. "#BaseHeight*3*mm" is two lengths multiplied together, hence an area. We can't round up areas.
Does this help?
Regarding your question on renaming, yes, we have another TODO to support this -- for now that is unfortunately a pain.
You're saying that it's important to appreciate that an equation may contain subexpressions with units from different domains -- length and area in your example. Though a single expression can refer to quantities from different domains, there are inherent mappings across domains that are intrinsic to the grammer. Other programming grammers may rely on the use of builtin functions to cross domains, like "ptIn3D = new Point(2,3,4)". However OnShape's grammer includes intrinsic operators that both create a measure in a domain, like "2mm", and cross domains, like "3 m^2 / (2 cm)". As you say, the grammer is sufficiently deterministic to allow measures to be expressed, to convert between measures within a domain, and to invoke intrinsic mappings across domains.
As such, each node in a tree that results either from the parsing of an expression or some other internal tree generator is a (value, domain) tuple. The problem with this representation is that it requires that all values be represented as a value normalized to the intrinsic unit of measure for that domain. For example, distance may well be represented internally as a floating point number of millimeters -- even if the distance were originally expressed in yards.
Yet you say OnShape can correctly interpret the expressions "ceil(3mm)" and "ceil(3ft)". How? Why? Is it that the single leaf-node trees that result from the parsing of "3mm" and "3ft" are actually (value, domain, unit) tuples that preserve values in the unit of measure they are expressed in? If so, then why can't all nodes of the tree be 3-tuples like that? This would allow significant syntactical simplification and and a more productive UX, because this set of intuitively unambiguous expressions could be accepted...
Variable #a = 2mm
Variable #b = ceil(#a*3)
- Bob
Now let's talk internal representations
You are right -- when we evaluate the expression "3 ft + 2 in", we internally store the value in meters and that it is a length -- (0.9652, meter) in your terms.
The problem with using something like the triplets (0.9652, meter, originallyExpressedInFeet) is that now 3 feet is not the same thing as 36 inches -- because they are stored as different triplets. Worse, if we do something like go with the unit that comes in some way first in the expression, we may get that "3 ft + 2 in" is different than "2 in + 3 ft" -- that's really unintuitive!
Here's another way to think about the problem with the triplets scheme: yes, when designing, you may want to write an expression like ceil(#a*3) where you know #a is a length expressed in millimeters. But the next person who looks at your model and needs to understand what's going on will have to think about not just the value of #a (the length it actually represents) but will have to track down the units it was originally expressed in. The way I think about expressions like "ceil(#a * 3 / mm) * mm" is that it's actually documenting what's going on: I'm taking the length represented by #a, counting how many millimeters it is, rounding that number up, and taking that many millimeters.
I don't know how unintuitive the user experience would actually be. Addition is a transitive operation. If the equation grammer is extended to include precedence rules related to units of measure, the distance value that results from both operations, "3 ft + 2 in" and "2 in + 3 ft", would be the same. What I'm suggesting would affect the third element of the 3-tuple -- the original unit of measure.
Let's get concrete. Suppose the OnShape equation grammer is updated with the addition of precedence rules for units of measure. Not to prejudice what those rules should be, but suppose a rule is added that says the unit of measure of the first term of an addition defines the result's unit of measure. With that rule in place, implemented using a 3-tuple like the one I've described, the separate results of these two equations would be...
"3 ft + 2 in" = (0.6604, distance, feet)
"2 in + 3 ft" = (0.6604, distance, inches)
Notice that the distance value is the same in both, because all distances are stored as quantities of meters. The only difference is the element that references the scale the value was originally expressed in by the first term. A grammer that includes a complete set of unit of measure rules would preserve this scale deterministically. So functions like ceil() would be able to properly handle all intuitively obvious expressions, like...
ceil(#a)
ceil(#a*3)
ceil(3*#a)
It may be worth explicitly saying that addition's transitive nature is unaffected by the grammer rules extensions. The extentions would also be fully backward compatible.
The rule extensions I'm suggesting would continue to allow explicit references to units of measure, while also allowing users the flexibility to capture the unit of measure in a single place (the variable definition) rather than repeating it in every variable reference. This would have potential benefits in some refactoring use cases.
- Bob
Consider the following possible syntax to see what I mean...
- Bob