Gitter chat: https://gitter.im/FreeCAD-ellipse/community
So, as I wanted to fix sketcher solver, and scaling constraint error functions didn't give good results, I now want to scale the dofs themselves. It seems doable within the current situation. However, since I am thinking too hard about the solver, I decided to try initiate the effort of rewriting the solver.
The primary goal is to have a standalone solver decoupled from Sketcher, ready for 3d assembly solving, and with full python bindings.
So, here we go.
Design goals (i.e., improvements over current solver).
* full python support (one should be able to implement a 2d/3d sketcher fully in python using the solver)
* python-powered constraint support (with inevitable speed problems, of course)
* externally extendable (i.e. no hard-coding of constraint types)
* reentrant solver.
That is, allow create multiple solvers and run them in parallel. That implies no writing to ParameterStore while solving.
* built-in parameter scaling factor support, to eliminate the size-of-sketch magnitude problem
* use DualNumbers in constraint-related math to implicitly compute derivatives
(was DeriVectors, pretty much the same stuff)
* don't evaluate constraint derivatives for parameters the constraint does not depend on, for speed, yet avoid branching.
(by using list of parameters returned by constraints, and stored indexes in lookup tables, we can selectively fill matrix elements with very little overhead)
* support rigid groups of geometry
This is a rough architecture I came up with to fit the goals. I'm open for discussion.
Code: Select all
class Parameter //parameter should always live in ParameterStore //Py: nothing, accessed via ParameterRef HParameterStore host double value double magnitude = 1.0 int par_index int masterindex //==par_index, unless the parameter was redirected std::string name class ParameterRef //Py: copy-construct HParameterStore host int par_index Parameter& operator* class ParameterStore //py: hosted by PyObject, no constructor on stack vector<Parameter> parameters vector<int> redirectionmap (key = index of parameter, value = index of master parameter) PyObject* self class HParameterStore //handle to ParameterStore //Py: nothing, it's a C++ wrapper around python object Py::Object store_py ParameterStore* store ParameterStore& operator* class ParameterSet //list of parameters. e.g., list of unknowns for solver //py: copy-construct? smart_ptr vector<ParameterRef> parameters smart_ptr vector<int> lut //key = parameter index. value = index into the set operator (retrieves the parameter) PyObject* pyobj class ParameterValueSet(ParameterSet) //py: construct around one on stack, with crash potential Eigen::vectorxD values Eigen::vector<double> duals //dual parts, for derivative computation operator (retrieves the alternate value, as values[lut[arg->masterindex]] if in set else arg->value()) class Solver: //py: hosted by PyObject, no constructor on stack ParameterSet unknowns Vector<HConstraint> constraints ...and heaps of other stuff PyObject* self class HSolver: Py::Object solver_py Solver* solver Solver& operator* class Constraint //py: hosted by PyObject, no constructor on stack PyObject* self int tag methods: vector<ParameterRef> params() //all params the constraint is using DualNumber error(ParameterValueSet &on) double maxStep(ParameterValueSet &on, ParameterValueSet &dir, double curstep) class HConstraint: Py::Object constraint_py Constraint* solver Constraint& operator* class ParaObject //base class for all objects built on parameters //py: copy-construct ParameterSet pvec //to be filled in by constructors static makeWithParams(ParameterStore params) 2D: class DualVector2 class ParaVector2(ParaObject) class ParaGeom2(ParaObject) class ParaPoint2(ParaGeom2) class ParaEdge2(ParaGeom2) class ParaFullEdge2(ParaEdge) class ParaTrimmedEdge2(ParaEdge) class PlacedGeometry(ParaGeom2) //to support rigid blocks, think assembly class PlacedPoint(PlacedGeometry) class PlacedEdge(PlacedGeometry) class ParaCurve2(ParaGeom2) class ParaLine2(ParaCurve2) class ParaCircle2(ParaCurve2) class ParaEllipse2(ParaCurve2) ... 3D: class DualVector3 class DualRotation class DualPlacement class ParaVector3(ParaObject) class ParaRotation(ParaObject) class ParaPlacement(ParaObject) class ParaGeom3(ParaObject) class PlacedShape(ParaGeom3) class ParaEdge3(ParaGeom3) class ParaFullEdge3(ParaEdge3) class ParaTrimmedEdge3(ParaEdge3) class ParaCurve3(ParaGeom3) class ParaLine3(ParaCurve3) class ParaCircle3(ParaCurve3) class ParaEllipse3(ParaCurve3) ... class ParaSurface3(ParaGeom3) class ParaPlane3(ParaGeom3) class ParaSphere3(ParaGeom3) ... Notes: DualVector, DualRotation, ParaVector2,3, ParaRotation, ParaPlacement etc should support assignment from and to their Base::<> twins
* typesystem integration
* add some storage facilities in geometry, constraints and parameters. For example, add a Py::Object data member. And probably it's good to have something for C++ too.
* (super optional) interactive solving support (i.e. manual iteration advancement)
* template constraint implementations for coordinates (e.g., CurveValueX and CurveValueY constraints share the implementation, and just return differences in respective coordinates as error their function)
Tasks (same number = can be done in parallel)
1. Set up new FreeCAD module
2. implement Parameter, ParameterRef, ParameterStore, HParameterStore, ParameterSet, ParameterValueSet
2. implement DualNumber math functions, DualVector2
3. implement ParaVector2,
4. Implement ParaGeom2 and derived, and constraints
4. reimplement solver, taking care to account for parameter scaling
5. Test the solver with python
6. rewrite Sketch.cpp to use the new solver
7. some good testing!
8. implement 3d placement, geometry and constraints
9. use in assembly workbench
Who is brave enough to join this? I feel like I can't lift it all on my own.