[WIP] Constrainator

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
abdullah
Veteran
Posts: 4935
Joined: Sun May 04, 2014 3:16 pm
Contact:

Re: [WIP] Constrainator

Post by abdullah »

I have decided to upload to GitHub the current version of Constrainator:
https://github.com/FreeCAD/FreeCAD/pull/1554

I think it could be merged, but as the PR says it could be possible to improve the python interfacing before doing so, as to avoid having to change anything build on it afterwards.

I am not posting videos, as the capabilities are substantially the ones shown above. I would like to comment on how it works, and particularly in how it can leveraged by Python users.

There are "simple" routines, that follow the following logic:

Detect - (Analyse) - [Get] - [Set] - Make

Parenthesis means not necessarily available for all the routines, brackets means they are available but need not be executed, depends on the application.

So basically you "Detect" a type of constraint or types of constraints and you "Make", i.e. you create the constraints detected. Different constraints or types of constraints may take different parameters for detection, and also have default values, but are first detected and then made.

The Get/Set enables the user to see the result of the detection and modify it before applying it. As said they are optional, as the make acts on the detect result directly if this is not needed. It gives you back a list of tuples, which is (First, FirstPos, Second, SecondPos), for cases where several different constraints are detected by a single detect-analyse processes, it has a fifth value which is the constraint Type.

The Analyse, which is not available for all the routines, operates in detected constraints of the same routine, to look for alternatives. For example, a general pointonpoint detection leads to a search for coincident constraints, which can be later run via Analyse if it is intended to convert endpoint coincidence to endpoint perpendicular and tangent constraints.

Neither the Detect, nor the Analyse, nor the Get steps modify the Sketch geometry. Make applies the constraints stored internally in the SketchAnalysis object.

The make takes one parameter, a boolean "onebyone", which enables the user to decide whether constraints should be added one by one, solving and checking for redundancies after each addition and removing redundancies if positive. EACH routing has its own DEFAULT value, so some routines default to true and others to false. The reason is that some routines are fine with "False" where others require a "True".

Currently you have these simple routines (the same parameters are used in Python):

Code: Select all

    int detectMissingPointOnPointConstraints(double precision = Precision::Confusion() * 1000, bool includeconstruction = true);
    void analyseMissingPointOnPointCoincident(double angleprecision = M_PI/8);
    int detectMissingVerticalHorizontalConstraints(double angleprecision = M_PI/8);
    int detectMissingEqualityConstraints(double precision);
So basically "precision" is a distance in "mm", it may define a radius around endpoints to make coincidents, or the maximum difference between two lines to consider them equal. "angleprecision" is an angle in radians, and can be the maximum slope for which a line will still be considered horizontal or vertical, or the maximum deviation between tangents on endpoints allowable to still consider curves tangent or perpendicular.

Then there is currently one complex routine that combines different simpler routines, which is called autoconstraint, and is shown in the videos above. It basically calls the different detection methods, in a particular order and orderly adds the constraints to avoid problems.

Code: Select all

int autoConstraint(double precision = Precision::Confusion() * 1000, double angleprecision = M_PI/20, bool includeconstruction = true);
There are some new helper functions (yes, exposed to Python too):

Code: Select all

void autoRemoveRedundants(bool updategeo);
int deleteAllConstraints();
They are self-explanatory.

Of course if you test it, I am curious of your feedback.

For the Python doc readers:
<Methode Name="autoconstraint">
<Documentation>
<UserDocu>
Automatic sketch constraining algorithm.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="detectMissingPointOnPointConstraints">
<Documentation>
<UserDocu>
Detects Missing Point On Point Constraints. The Detect step just identifies possible missing constraints.
The result may be retrieved or applied using the corresponding Get / Make methods.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="analyseMissingPointOnPointCoincident">
<Documentation>
<UserDocu>
Analyses the already detected Missing Point On Point Constraints to detect endpoint tagency/perpendicular.
The result may be retrieved or applied using the corresponding Get / Make methods.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="detectMissingVerticalHorizontalConstraints">
<Documentation>
<UserDocu>
Detects Missing Horizontal/Vertical Constraints. The Detect step just identifies possible missing constraints.
The result may be retrieved or applied using the corresponding Get / Make methods.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="detectMissingEqualityConstraints">
<Documentation>
<UserDocu>
Detects Missing Equality Constraints. The Detect step just identifies possible missing constraints.
The result may be retrieved or applied using the corresponding Get / Make methods.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="makeMissingPointOnPointCoincident">
<Documentation>
<UserDocu>
Applies the detected / set Point On Point coincident constraints. If the argument is True, then solving and redundant removal is done after each individual addition.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="makeMissingVerticalHorizontal">
<Documentation>
<UserDocu>
Applies the detected / set Vertical/Horizontal constraints. If the argument is True, then solving and redundant removal is done after each individual addition.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="makeMissingEquality">
<Documentation>
<UserDocu>
Applies the detected / set Equality constraints. If the argument is True, then solving and redundant removal is done after each individual addition.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="autoRemoveRedundants">
<Documentation>
<UserDocu>
Removes constraints currently detected as redundant by the solver. If the argument is True, then the geometry is updated after solving.
</UserDocu>
</Documentation>
</Methode>

<Attribute Name="MissingPointOnPointConstraints" ReadOnly="false">
<Documentation>
<UserDocu>
returns a list of (First FirstPos Second SecondPos Type) tuples with all the detected endpoint constraints.
</UserDocu>
</Documentation>
<Parameter Name="MissingPointOnPointConstraints" Type="List"/>
</Attribute>
<Attribute Name="MissingVerticalHorizontalConstraints" ReadOnly="false">
<Documentation>
<UserDocu>
returns a list of (First FirstPos Second SecondPos Type) tuples with all the detected vertical/horizontal constraints.
</UserDocu>
</Documentation>
<Parameter Name="MissingVerticalHorizontalConstraints" Type="List"/>
</Attribute>
<Attribute Name="MissingLineEqualityConstraints" ReadOnly="false">
<Documentation>
<UserDocu>
returns a list of (First FirstPos Second SecondPos) tuples with all the detected line segment equality constraints.
</UserDocu>
</Documentation>
<Parameter Name="MissingLineEqualityConstraints" Type="List"/>
</Attribute>
<Attribute Name="MissingRadiusConstraints" ReadOnly="false">
<Documentation>
<UserDocu>
returns a list of (First FirstPos Second SecondPos) tuples with all the detected radius constraints.
</UserDocu>
</Documentation>
<Parameter Name="MissingRadiusConstraints" Type="List"/>
</Attribute>
abdullah
Veteran
Posts: 4935
Joined: Sun May 04, 2014 3:16 pm
Contact:

Re: [WIP] Constrainator

Post by abdullah »

User avatar
easyw-fc
Veteran
Posts: 3633
Joined: Thu Jul 09, 2015 9:34 am

Re: [WIP] Constrainator

Post by easyw-fc »

abdullah wrote: Sat Jul 14, 2018 8:15 am AppImage:
https://github.com/abdullahtahiriyo/Fre ... ator_v0.01
Hi @abdullah
is it possible to apply only coincidence constraint or only horizontal or vertical constraints?

Code: Select all

App.ActiveDocument.Sketch.autoconstraint(distance, angle)
abdullah
Veteran
Posts: 4935
Joined: Sun May 04, 2014 3:16 pm
Contact:

Re: [WIP] Constrainator

Post by abdullah »

easyw-fc wrote: Wed Oct 03, 2018 2:23 pm
abdullah wrote: Sat Jul 14, 2018 8:15 am AppImage:
https://github.com/abdullahtahiriyo/Fre ... ator_v0.01
Hi @abdullah
is it possible to apply only coincidence constraint or only horizontal or vertical constraints?

Code: Select all

App.ActiveDocument.Sketch.autoconstraint(distance, angle)
Hi Maurice!

Yes it is. Look here:
https://github.com/FreeCAD/FreeCAD/pull ... 09c57f9c62

It is very modular, because when you apply constraints, the geometry moves, so it is possible to first detect the different types of constraints that you want to apply, and after the different detections, you apply them. I paste the c++ versions because they are better documents and to see the parameters, but Python should be the same.

ActiveSketch.detectMissingPointOnPointConstraints(double precision = Precision::Confusion() * 1000, bool includeconstruction = true)

ActiveSketch.detectMissingVerticalHorizontalConstraints(double angleprecision = M_PI/8)

you may need the analyze if you want to detect endpoint tangencies for the coincident case. Read the constructor in the link above. If you do not want tangencies, omit the analyze step.

Then:

Activesketch.makeMissingPointOnPointCoincident(bool onebyone = false)

ActiveSketch.makeMissingVerticalHorizontal(bool onebyone = false)

For make "onebyone" means to solve after each addition. For coincidents and horizontals it is not useful (at least if you start from not having any constraint), but for "equality" constraints it is necessary, as after each single addition you may end up with redundants that need to be removed.

So, let's say, for the most simple case, first you detect, then you make.

Autoconstraint does this (it should give you an idea of how to use combinations of constraints):

Code: Select all

int SketchAnalysis::autoconstraint(double precision, double angleprecision, bool includeconstruction)
{
    App::Document* doc = sketch->getDocument();
    doc->openTransaction("delete all constraints");
    // We start from zero
    sketch->deleteAllConstraints();

    doc->commitTransaction();

    int status, dofs;

    solvesketch(status,dofs,true);

    if(status) {// it should not be possible at this moment as we start from a clean situation
        THROWMT(Base::RuntimeError, QT_TRANSLATE_NOOP("Exceptions", "Autoconstrain error: Unsolvable sketch without constraints.\n"))
    }

    // STAGE 1: Vertical/Horizontal Line Segments
    int nhv = detectMissingVerticalHorizontalConstraints(angleprecision);

    // STAGE 2: Point-on-Point constraint (Coincidents, endpoint perp, endpoint tangency)
    // Note: We do not apply the vertical/horizontal constraints before calculating the pointonpoint constraints
    //       as the solver may move the geometry in the meantime and prevent correct detection
    int nc = detectMissingPointOnPointConstraints(precision, includeconstruction);

    if (nc > 0) // STAGE 2a: Classify point-on-point into coincidents, endpoint perp, endpoint tangency
        analyseMissingPointOnPointCoincident(angleprecision);

    // STAGE 3: Equality constraint detection
    int ne = detectMissingEqualityConstraints(precision);

    Base::Console().Log("Constraints: Vertical/Horizontal: %d found. Point-on-point: %d. Equality: %d\n", nhv, nc, ne);

    // Applying STAGE 1, if any
    if (nhv >0 ) {
        App::Document* doc = sketch->getDocument();
        doc->openTransaction("add vertical/horizontal constraints");

        makeMissingVerticalHorizontal();

        // finish the transaction and update
        doc->commitTransaction();

        solvesketch(status,dofs,true);

        if(status == -2) { // redundants
            autoRemoveRedundants(false);
            solvesketch(status,dofs,false);
        }

        if(status) {
            THROWMT(Base::RuntimeError, QT_TRANSLATE_NOOP("Exceptions", "Autoconstrain error: Unsolvable sketch after applying horizontal and vertical constraints.\n"))
        }
    }

    // Applying STAGE 2
    if(nc > 0) {
        App::Document* doc = sketch->getDocument();
        doc->openTransaction("add coincident constraint");

        makeMissingPointOnPointCoincident();

        // finish the transaction and update
        doc->commitTransaction();

        solvesketch(status,dofs,true);

        if(status == -2) { // redundants
            autoRemoveRedundants(false);
            solvesketch(status,dofs,false);
        }

        if(status) {
            THROWMT(Base::RuntimeError, QT_TRANSLATE_NOOP("Exceptions", "Autoconstrain error: Unsolvable sketch after applying point-on-point constraints.\n"))
        }
    }

    // Applying STAGE 3
    if(ne > 0) {
        App::Document* doc = sketch->getDocument();
        doc->openTransaction("add equality constraints");

        try {
            makeMissingEquality();
        }
        catch(Base::RuntimeError e)
        {
            doc->abortTransaction();
            throw;
        }

        // finish the transaction and update
        doc->commitTransaction();

        solvesketch(status,dofs,true);

        if(status == -2) { // redundants
            autoRemoveRedundants(false);
            solvesketch(status,dofs,false);
        }

        if(status) {
            THROWMT(Base::RuntimeError, QT_TRANSLATE_NOOP("Exceptions", "Autoconstrain error: Unsolvable sketch after applying equality constraints.\n"))
        }
    }


    return 0;
}
Python:
https://github.com/FreeCAD/FreeCAD/pull ... bbc9ea8ff7

Much luck.

Come back to me if you see strange things :)
User avatar
easyw-fc
Veteran
Posts: 3633
Joined: Thu Jul 09, 2015 9:34 am

Re: [WIP] Constrainator

Post by easyw-fc »

abdullah wrote: Wed Oct 03, 2018 7:08 pm
Much luck.

Come back to me if you see strange things :)
Thx a lot for your FC code and your clarifications.
I'll work on those :D

Maurice
Post Reply