Expressions... again...

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
eivindkvedalen
Posts: 602
Joined: Tue Jan 29, 2013 10:35 pm

Expressions... again...

Postby eivindkvedalen » Fri Dec 12, 2014 12:22 am

Hi all,

I've had another try at implementing expressions for properties now, and I have a (start for a) solution I would like to present. I've pushed it to github: https://github.com/eivindkv/free-cad-co ... pressions2

This is somewhat related to the Spreadsheet discussion about having a PropertyController. Adding expressions would easily kill the need for a way to push property values from a spreadsheet, as it could be pulled from it instead.

The commit is rather large, mostly dues to tests/use-cases, and consists of the following:
1) Property dependencies:
- A way to describe dependencies between properties. This is completely within the Property.h/cpp files, and is basically implemented as an extra field in the Property class. I have added a Path class here, to be able to describe dependencies on sub-elements of a property, e.g a position of a Placement, or a single element of an array.
- The DocumentObject class now takes the dependencies into account when the computation graph is done, by investigating the properties' dependencies. This is also done in its isTouched function. This very much resembles ickby's solution (discussed in a different but recent thread here).

A notable difference from ickby's solution is that a property can have an arbitrary number of dependencies in my implementation.

2) Expressions
I have taken my Expression classes from the Spreadsheet module, and slightly modified them. This is how the "raw" expressions are done. One notable change is that the syntax to refererence a property from a different document object is changed to object:property, not object.property, e.g to reference the Length of a Pad object, one uses 'Pad:Length'. This is to make the 'object' unique, so it cannot be interpreted as a compound property with a member.

To collect them and get them evaluated correctly and at the right time, they are all stored in a PropertyExpressionEngine object. This is basically a map with Path -> Expression, where Path is a path to a property in a DocumentObject (its owner). The ExpressionEngine is responsible for figuring out if there are internal cycles among th expressions, and also the correct evalutaion order.

From Python, expressions can be set using the setExpression method in DocumentObjects, like App.ActiveDocument.Pad.setExpression('Length', '2 * 10mm')

3) Support in Gui::InputField

This is added by the function bind(object, path). When this is invoked, the input field gets bound to this document object and path, and the underlying property gets instant support for expressions.

4) Support in Pad and Pocket. I have added bind(...) to these two for testing purposes.

5) Support in Sketcher. I have added bind(...) here to be able to use expressions for constraint datums. Datums are accessed by their name in other expressions. The Sketcher Workbench is particularly interesting, because the datum values are stored in an array, but these elements should from a user's perspective be identified by its name. This is done by reimplementing setValue/getValue/getPropertyByPath from DocumentObject (these are new and virtual functions in this class).

A small example on how to use: Create a rectangle with a constraint on width and height. Rename constraints to Width and Height, instead of their default names like Constraint9. To make a box where the height is always half of its width, edit the height constraint, and input 'Width/2'. The constraint should be recomputed automatically, and it will also change whenever you change the Width constraint.

Another example: To make a pocket always be 5mm above the bottom of a pad, its Length can be set to 'Pad:Length - 5mm'.

6) The generic propery editor switches the field to read-only if an expression is attached to it (this is for test only, ideally the expression should be show in an editable way here).

I have also added Save/Restore functions, but these do not work as intended yet, and they might or might not crash when invoked (it depends on how complex the relationships are in the model). Also, for the time being, the code is not very forgiving if you try to delete objects that have dependencies. So, there are some rough edges here and there, but this is more a proof of concept. Let me know what you think, if this is a wanted feature, and if this is a viable implementation strategy.

Best regards,
Eivind
User avatar
DeepSOIC
Posts: 7479
Joined: Fri Aug 29, 2014 12:45 am
Location: Saint-Petersburg, Russia

Re: Expressions... again...

Postby DeepSOIC » Fri Dec 12, 2014 12:56 am

Wow! This IS a wanted feature for sure! Can I make references between sketches? (i.e. setting a with of a rectangle equal to a radius constraint from another sketch?)
User avatar
saso
Posts: 1418
Joined: Fri May 16, 2014 1:14 pm
Contact:

Re: Expressions... again...

Postby saso » Fri Dec 12, 2014 1:09 am

One thought, since FreeCAD already supports on-the-fly calculations (ex. putting in the value 3*12 will calculate it and store the result as the property value), would it not make sense to somehow distinguish the expressions input from this default existing functionality since the idea is that expressions are stored permanently as the property value. Would it not make sense for all expressions to begin with "=" ? It seems to me that this could also be a good and simple way to control if the input should be treated as expression or as a standard input and for resetting it? :|

But yes, this feature is absolutely wanted, Thanks for working on implementing it to both you and ickby!
eivindkvedalen
Posts: 602
Joined: Tue Jan 29, 2013 10:35 pm

Re: Expressions... again...

Postby eivindkvedalen » Fri Dec 12, 2014 11:07 am

DeepSOIC wrote:Wow! This IS a wanted feature for sure! Can I make references between sketches? (i.e. setting a with of a rectangle equal to a radius constraint from another sketch?)
You can make a reference to another sketch, but it will not set it as a constraint in the sense that it would invoke a solver to make them equal. Basically it will only pull the datum from one sketch to another, and then make sure they will stay in sync. This means that during the recompute stage of the document, your sketch may get its datum updated, and that might trigger the solver for that sketch to satisfy its other constraints.

Eivind
eivindkvedalen
Posts: 602
Joined: Tue Jan 29, 2013 10:35 pm

Re: Expressions... again...

Postby eivindkvedalen » Fri Dec 12, 2014 11:17 am

saso wrote:One thought, since FreeCAD already supports on-the-fly calculations (ex. putting in the value 3*12 will calculate it and store the result as the property value), would it not make sense to somehow distinguish the expressions input from this default existing functionality since the idea is that expressions are stored permanently as the property value. Would it not make sense for all expressions to begin with "=" ? It seems to me that this could also be a good and simple way to control if the input should be treated as expression or as a standard input and for resetting it? :|

But yes, this feature is absolutely wanted, Thanks for working on implementing it to both you and ickby!
We definitely need to think more about the GUI bit of this. I've only done a minimal amount of changes to the GUI to be able to test it myself. For instance, we could consider that pressing the = key would trigger a different mode that allows you to enter expressions, maybe even in a pop-up, so we can give meaningful error messages in a better way.

Another thing is that if a user types e.g 3 * 12, he most likely has a good reason for it, and I'm not sure that we really should compute the final value and store that and throw away the expression. There are more non-obvious, but constant values, that shows this, e.g if the user types 100 mm * cos(45deg). This is also constant, but there is a thought process by the user behind the final number, which I think we shouldn't throw away.

Eivind
ickby
Posts: 2940
Joined: Wed Oct 05, 2011 7:36 am

Re: Expressions... again...

Postby ickby » Fri Dec 12, 2014 12:10 pm

That sounds awesome! I don't have time to take a look over the code over the weekend, but hopefully during the next week. I'm curious how you did implement it :)
User avatar
DeepSOIC
Posts: 7479
Joined: Fri Aug 29, 2014 12:45 am
Location: Saint-Petersburg, Russia

Re: Expressions... again...

Postby DeepSOIC » Fri Dec 12, 2014 3:44 pm

eivindkvedalen wrote:You can make a reference to another sketch, but it will not set it as a constraint in the sense that it would invoke a solver to make them equal. Basically it will only pull the datum from one sketch to another, and then make sure they will stay in sync. This means that during the recompute stage of the document, your sketch may get its datum updated, and that might trigger the solver for that sketch to satisfy its other constraints.
This sounds like perfectly enough, except I would sometimes want to reference e.g. a length of a line that is constrained indirectly... But simply being able to pull a value of another datum is very good IMO!
eivindkvedalen
Posts: 602
Joined: Tue Jan 29, 2013 10:35 pm

Re: Expressions... again...

Postby eivindkvedalen » Fri Dec 12, 2014 9:31 pm

DeepSOIC wrote:This sounds like perfectly enough, except I would sometimes want to reference e.g. a length of a line that is constrained indirectly... But simply being able to pull a value of another datum is very good IMO!
I've thought a bit more about this, and it is actually quite simple to do. If you add some kind of ruler or gauge capability to the Sketcher Workbench, e.g a map string -> quantity property, which the solver updates after it is done, then these can be referenced by other sketches just like the other "normal" datums.

Eivind
User avatar
DeepSOIC
Posts: 7479
Joined: Fri Aug 29, 2014 12:45 am
Location: Saint-Petersburg, Russia

Re: Expressions... again...

Postby DeepSOIC » Fri Dec 12, 2014 10:35 pm

I actually thought on implementing a universal constraint datum determination, which would simplify datum constraint creation routines. But I'm afraid I won't do it myself.
cmeyer
Posts: 1
Joined: Thu Dec 18, 2014 4:59 pm

Re: Expressions... again...

Postby cmeyer » Thu Dec 18, 2014 6:22 pm

To me, this is the single most needed improvement to FreeCAD. I am working on a RepRap 3d printer aimed at education where my high school students can try changing one part of the design and then test how the performance changes. To do this, the design has to be truly parametric and the software free. Without this addition, my only other option OpenSCAD, but that is not very student friendly. Some questions/ ideas about the final implementation:

1. Will there be a way of depending on an value from a different file? This would allow a project to have a master part with only a spreadsheet in it that allows me to define the main settings for my entire project. I could change the linear motion rod diameter for my 3d printer from 8mm to 10mm in this file and run some kind of build script, and 5 different part files that interface with the rods would update at once without be having to edit 10 different sketches in 5 different files. This is the pattern used in the Prusa 3d printers designed in OpenSCAD.

2. Will there eventually be a way to use conditional statements in expressions? For example in my project, if the linear rods are 8mm then the linear barring will need a 15mm hole, but if I am using 10mm rods, it will need a 19mm hole. Also, there will be several options that will be on or off and we need to tweak dimensions accordingly.


3. I agree with the previous comments that when someone enters 6/3 it should be stored as 6/3 and not as 2.

I will be testing your code and am interested in helping make this work.