==========================================
fixes#6174
Problem
=======
The popularity contest heuristic was designed to assume that removing one solver constraint from a redundant group would potentially satisfy it.
This means that for sketcher constraints comprising several solver constraints, the sketcher constraint would never be notified as redundant, but
always partially redundant (where the case may be that it is redundant or that it is partially redundant). This happens because after removing one
solver constraint, it may happen:
(a) that no other solver constraint corresponding to the sketcher constraint remains in the redundant group (so it is indeed partially redundant)
(b) that at least one other solver constraint corresponding to the sketcher constraint remains in the redundant group (so if all solver constraints
remain in the redundant group, the sketcher constraint is actually redundant). This happens because solver constraints of a single sketcher constraint
are orthogonal and consequently, a conflict (or redundancy) emanating from removing one of them cannot actually satisfy the group, as it has no effect
on the other.
Solution
========
When popularity constraint decides on one solver constraint, remove any other solver constraint of the same tag (i.e. same sketcher constraint) that is
present in the conflict group (case b). This does not affect case a, because the solver constraint that is not redundant is not present in the conflict
(redundancy) group.
=======================================
Refactor:
- Substitute the use of variadic templates with parameter packs.
- Use recently incorporated external library "fmt" to handle printf like formating.
- Extensive cleaning of pragmas and unnecessary forward declarations.
- Parameter packs and libfmt provide a much stronger type checking now, so
conversions that are by standard implicit as bool to int need an explicit static_cast
to avoid compilation warnings.
Extension:
- Include a notifier field, so that the originator of the message can be provided. E.g. Document#DocumentObject
- Include a new type of message called CriticalMessage, this message is intended to have
special behaviour in the future. Namely, it will be used to notify forward compatilibity issues.
It will be used to substitute the current signal/slot mechanism.
- Include two new types of messages for user notifications (Notification and TranslatedNotification). This messages
will be use to convey UI notifications intended for the user (such as non-intrusive message about the usage of a tool). There
are two versions to mark whether the string provided as a message is already translated or not. When using the console system for
notifications, these notifications may originate from the App or the Gui. In the former, it is generally the case that the strings
of messages are not (yet) translated (but they can be marked with QT_TRANSLATE_NOOP). In the latter, often the messages to be provided
are already translated.
Python support for CriticalMessage, Notification and TranslatedNofification, including shortcuts:
Crt = FreeCAD.Console.PrintCritical
Ntf = FreeCAD.Console.PrintNotification
Tnf = FreeCAD.Console.PrintTranslatedNotification
======================================================================
1. Instead of excluding internal alignment constraints from popularity candidate selection, exclude them from the group altogether. This
ensures no group is non-empty with uneligible candidates, which prevents an infinite loop in uncommon circumnstances (when DoFs collapse).
2. Ensure no internal alignment constraint is identified when conflict ensues.
Comments by @abdullahtahiriyo.
Remove default values and smaller constructor for `CenterOfGravity` and
`WeightedLinearCombination` constraints.
Clarify comments.
Improve readability of `CenterOfGravity` and `WeightedLinearCombination`
constraints.
Also squashes:
[Sketcher] Create center of gravity constraint in planegcs
[Sketcher] typo
[Sketcher] Use accurate "weights" for knots
By weights we mean the linear combination factor B_i(x) such that
spline(x) = sum(pole_i * B_i(x)) for _non-rational_ splines.
[Sketcher] Use more appropriate weights for knots
These are relevant for knots _away_ from any ends (and possibly other high
multiplicity knots).
[Sketcher] Make COG constraint weights user-definable
[Sketcher] Make `flattenedknots` for periodic B-Splines
[Sketcher] Fix incorrect setup of `flattenedknots`
Without ensuring enough space, iterators become invalid. These iterators are
needed because for periodic B-splines we need to pad flattenedknots with offset
values within flattenedknots.
Apparently there is still some iterator issues even after the reserve. Just use
fresh vectors instead.
[Sketcher] Apply knot constraints by parameter
Hopefully this will allow directly applying constraints on knots.
[Sketcher] Disable some knot updating
[Sketcher] Use center of gravity constraint on knots
[Sketcher] Fix knot COG constraint for periodic splines
[Sketcher] Add start/end point of periodic spline to solver
This removes the trouble of transferring constraints to the underlying knot.
[Sketcher] Support knot constraints on rational B-splines
[Sketcher] Remove virtual from overridden methods in planegcs
Follow 0penbrain's comments
[Sketcher][planegcs] Use `unsigned int` in signatures
Also `size_t` at places
Suggestions by @abdullahtahiriyo
================================================================
Master has a problem in that internal alignment constraints are suggested to the user for removal.
This is fundamentally wrong, as an internal alignment constraint are an inherent part of the geometry. They cannot be the ones suggested for removal.
The popularity contest algorithm is an heuristic algorithm that determines which redundant/conflicting constraints should be proposed for removal.
Basically, the algorithm works on groups of redundant/conflicting constraints detected via the QR decomposition. A constraint may belong to more than one group.
The algorithm runs some heuristics, each constraint scoring a value, the one constraint from each group scoring the highest is proposed (is more popular and wins the contest).
This PR documents the algorithm, and adds a further condition, that internal alignment constraints are never proposed.
As the solver works with solver constraints as opposed to the sketcher, which works with sketcher constraints, information about whether a solver constraint originated from a
sketcher constraint that is internal alignment is necessary. So the solver constraint is extended to accomodate this piece of information.
As a bonus, it fixes a bug. Solver constraints carry information of the ID of the corresponding sketcher constraint in their tag. Knots are not currently implemented as constraints.
However, the tag index was not being update. This caused the popularity contest to provide wrong suggestions despite good detection.
Some equations were mentioned by just the number without telling the resource they were from. The book was found couple years ago but not mentioned here.
* Missing reference in range-for with non trivial type [-Wclazy-range-loop-reference]
* Unused QString [-Wclazy-unused-non-trivial-variable]
* Missing emit keyword on signal call [-Wclazy-incorrect-emit]
* Don't call QList::operator[]() on temporary [-Wclazy-detaching-temporary]
* Use multi-arg instead [-Wclazy-qstring-arg]
* Maybe you meant to call ViewProvider2DObjectGrid::onChanged() instead [-Wclazy-skipped-base-method]
========================================
Previous versions relied on a heuristic that proved insufficient for cummulative use of the Block constraint.
The effect is that Block constraints stopped blocking, which is a major bug, as it lead to inadvertedly moving
geometry that was supposed to be blocked.
Fixes:
https://forum.freecadweb.org/viewtopic.php?f=13&t=53515&start=30#p461215
(Thanks Chaospilot)
Know problems with old block constraint (v0.18):
1. If driving constraints were present, they were ignored if inserted before the block constraint (to avoid
redundancy/conflicting). They resulted in
Principles of Working of the new block constraint:
1. Handling of the new block constraint is based two processes, a pre-analysis and a post-analysis. Pre-analysis
works *before* diagnosing the system in the solver. Post-analysis works *after* diagnosing the system in the solver.
2. Pre-analysis is directed to detect geometries affected *exclusively* by a block constraint. This is important
because these geometries can be pre-fixed when creating the solver geometry and constraints before GCS::diagnose()
via initSolution() AND because if no other constraint affects the geometry, the geometry parameters won't even appear
in the Jacobian of GCS, so they won't be reported as dependent parameters (for which post-analysis would be of no use).
3. Post-analysis is directed to detect Geometries affected *not only* by a block constraint. This is important
because pre-fixing these geometries would lead to redundant constraints. The post-analysis, enables to fix just the
parameters that fulfil the dependacy groups.
4. Post-analysis basically identifies which parameters shall be fixed to make geometries having blocking constraints
fixed, while not leading to redundant/conflicting constraints. These parameters must belong to blocked geometry. This
is, groups may comprise parameters belonging to blocked geometry and parameters belonging to unconstrained geometry. It
is licit that the latter remain as dependent parameters. The former are referred to as "blockable parameters". Extending
this concept, there may be unsatisfiable groups (because they do not comprise any bloackable parameter), and it is the
desired outcome NOT to satisfy such groups. It must be emphasised that there is not a single combination of fixed parameters
from the blockable parameters that satisfy all the dependency groups. However:
1) some combinations do not satisfy all the dependency groups that must be satisfied (e.g. fixing one group containing
two blockable parameters with a given one may result in another group, fixable only by the former, not to be satisfied).
This leads, in a subsequent diagnosis, to satisfiable unsatisfied groups.
2) some combinations lead to partially redundant constraints, that the solver will silently drop in a subsequent diagnosis,
thereby reducing the rank of the system fixing less than it should.
5. The implementation rationale is as follows:
1) The implementation is on the order of the groups provided by the QR decomposition used to reveal the parameters
(see System::identifyDependentParameters in GCS). Zeros are made over the pilot of the full R matrix of the QR decomposition,
which is a top triangular matrix.This, together with the permutation matrix, allow to know groups of dependent parameters
(cols between rank and full size). Each group refers to a new parameter not affected by the rank in combination with other free
parameters intervening in the rank (because of the triangular shape of the R matrix). This results in that each the first column
between the rank and the full size, may only depend on a number of parameters, while the last full size colum may dependent on
any amount of previously introduced parameters.
2) Thus the rationale is to start from the last group (having **potentially** the larger amount of parameters) and selecting as blocking
for that group the latest blockable parameter. Because previous groups do not have access to the last parameter, this can never
interfere with previous groups. However, because the last parameter may not be a blockable one, there is a risk of selecting a parameter
common with other group, albeit the probability is reduced and probably (I have not demonstrated it though and I am not sure), it
systematically leads to the right solution in one iteration.
GCS: Change dependency group from std::set to std::vector to prevent reordering of parameters.
=============================================================================
The reduced Jacobian defaults to the size of the full Jacobian, where driven constraints
and parameters are not considered to build the reduced Jacobian and thus empty rows remain.
This is generally not a problem as QR decomposition ignores this rows, except in case where
only driven constraints exist. In such a case no QR decomposition is necessary, and in fact
relying on the size of the QR decomposition leads to the incorrect result that the sketch
is fully constraint, when the opposite is true.
This commit resizes an effectively empty reduced Jacobian to avoid this issue.
Fixes the problem that a line with a single reference constraint is shown as fully constraint.
======================================
Explicitly indicate that if parameter diagnosis must be silent=false in order to debug,
then the lauch policy during the debug must be set to deferred in order to avoid concurrent
access to Base::Console which is not thread-safe.
================================================================================================================
std:async can take two policies one that forces the task to be run in parallel another (deferred) that runs the task
when wait is called on the future (kind of lazy evaluation).
Default policy is let the system decide (an or of both policies). Nobody is a better position than the system to know
its load and whether it is possible to run the task in parallel or not.
If the system cannot allocate a thread and parallel processing is enforced (as before this commit), then it will throw an
exception. In the present case we would catch it and run the task sequencially. Thus, it makes sense to let the system
decide from the beginning and save the exception and the handling.
In tests I have only managed to see it run in parallel with the default policy.
=================================================================
Diagnose constraints do not include among others driven constraints. This
function allows to know whether the solver is considering an empty matrix.