Feature development : chamfer on sketch tool and complex fillet

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!
Post Reply
User avatar
paddle
Veteran
Posts: 1412
Joined: Mon Feb 03, 2020 4:47 pm

Feature development : chamfer on sketch tool and complex fillet

Post by paddle »

From former discussion there : https://forum.freecadweb.org/viewtopic. ... 39#p367439
I discussed to make a more complex fillet tool. I open a topic here as I have some questions about it's development.

Aim : Add a dropdown to the current fillet to add more options :
- Chamfer
- Poly chamfer (similar to polygon you can put the number of angle you want)
- Inward fillet and polychamfer: symetric of fillet/chamfers by the chamfer line.
Image

So in order to develop this feature, I started to look how current fillet is implemented. Also how the polygon is implemented in GUI. So I made all the GUI side (svg icons, CommandCreateGeo.cpp, workbench.cpp...) and now I'm at the App side for which I'm meeting some difficulties.

So the general idea is that the fillet handler from CommandCreateGeo.cpp has an additional int argument called nof_angles, which is the number of angle. Values are :
1 : fillet
-1 : inward fillet
2 and -2 : chamfer
3 or more : polychamfer
-3 or less : inward poly chamfer.

So on the App side, fillet tool function is in SketchObject.cpp

As in all cases we need the fillet arc, we add at the end of the function additional code for the other cases. And this code will put the fillet arc as construction and create necessary components.

I made the logic of the code, but my issue is that I have difficulties to make the correct syntax. As there are things I don't understand. For example:

Code: Select all

        // create arc from known parameters and lines
        *arc = Part::createFilletGeometry(lineSeg1, lineSeg2, filletCenter, radius);
        if (arc) {
	    ...
            Part::Geometry *newgeo = arc;
            filletId = addGeometry(newgeo);
            }
- What is the difference between filletID and arc?
- Why we create new geometry from arc, then add this geometry and get this filletID int?
- When should we use arc GeomArcOfCircle and when should we use filletID Int?
- filletID is the identifier of the fillet geometry that we see on screen if I'm correct.
- If I further modify arc, will changes be done to filletID too? For example if I do

Code: Select all

arc->Construction=true;
After filletID is already created. Will the construction be applied on filletID ?
- If I want to make a new arc symmetric to the fillet arc, should I use the arc object or the filletID?

- Also I don't understand why sometime points are PointPos or Base::Vector3d

Code: Select all

            Part::GeomArcOfCircle *symArc = new Part::GeomArcOfCircle();
            symArc->setRadius(arc->getRadius()); //do we need to set values as we'll add constraint after?
?            Base::Vector3d symArcCenter;
?            symArc->setCenter(symArcCenter);
?            double startAngle, endAngle;
?            arc->getRange(startAngle, endAngle, /*emulateCCWXY=*/true);
?            symArc->setRange(startAngle+M_PI, endAngle+M_PI, /*emulateCCWXY=*/true);//do we need to set values as we'll add constraint after?
- When I make a new arc, does I have to initialize it's center point, range, radius or if I only add all constraints it will define them automatically?

Code: Select all

            //Constraint 1 : center of arc and center of symArc symmetric by arcLine
            Sketcher::Constraint *sym1 = new Sketcher::Constraint();
            sym1->Type = Sketcher::Symmetric; //symmetric is two point and a line. So we need to use center of arcs and arcLine.
            sym1->First = filletId;
?            sym1->FirstPos = center;
            sym1->Second = symFilletId;
?            sym1->SecondPos = center;
            sym1->Third = arcLineId;
            addConstraint(sym1);
            delete sym1;
- How to make get PointPos of the center of arc? Is that correct? I searched the code but could not find any reference for it.



If someone has courage to fix my syntax the feature should be working then. My current function with bad syntax :

Code: Select all

int SketchObject::fillet(int GeoId1, int GeoId2,
                         const Base::Vector3d& refPnt1, const Base::Vector3d& refPnt2,
                         double radius, int nofAngles, bool trim)
{
    if (GeoId1 < 0 || GeoId1 > getHighestCurveIndex() ||
        GeoId2 < 0 || GeoId2 > getHighestCurveIndex())
        return -1;

    const Part::Geometry *geo1 = getGeometry(GeoId1);
    const Part::Geometry *geo2 = getGeometry(GeoId2);
    Part::GeomArcOfCircle *arc;
    int filletId;
	bool FilletCreated = 0;
    if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
        geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId() ) {
        const Part::GeomLineSegment *lineSeg1 = static_cast<const Part::GeomLineSegment*>(geo1);
        const Part::GeomLineSegment *lineSeg2 = static_cast<const Part::GeomLineSegment*>(geo2);

        Base::Vector3d filletCenter;
        if (!Part::findFilletCenter(lineSeg1, lineSeg2, radius, refPnt1, refPnt2, filletCenter))
            return -1;
        Base::Vector3d dir1 = lineSeg1->getEndPoint() - lineSeg1->getStartPoint();
        Base::Vector3d dir2 = lineSeg2->getEndPoint() - lineSeg2->getStartPoint();

        // the intersection point will and two distances will be necessary later for trimming the lines
        Base::Vector3d intersection, dist1, dist2;

        // create arc from known parameters and lines
        *arc = Part::createFilletGeometry(lineSeg1, lineSeg2, filletCenter, radius);
        if (arc) {
            // calculate intersection and distances before we invalidate lineSeg1 and lineSeg2
            if (!find2DLinesIntersection(lineSeg1, lineSeg2, intersection)) {
                delete arc;
                return -1;
            }
            dist1.ProjectToLine(arc->getStartPoint(/*emulateCCW=*/true)-intersection, dir1);
            dist2.ProjectToLine(arc->getStartPoint(/*emulateCCW=*/true)-intersection, dir2);
            Part::Geometry *newgeo = arc;
            filletId = addGeometry(newgeo);
            if (filletId < 0) {
                delete arc;
                return -1;
            }
        }
        else
            return -1;

        if (trim) {
            PointPos PosId1 = (filletCenter-intersection)*dir1 > 0 ? start : end;
            PointPos PosId2 = (filletCenter-intersection)*dir2 > 0 ? start : end;

            delConstraintOnPoint(GeoId1, PosId1, false);
            delConstraintOnPoint(GeoId2, PosId2, false);
            Sketcher::Constraint *tangent1 = new Sketcher::Constraint();
            Sketcher::Constraint *tangent2 = new Sketcher::Constraint();

            tangent1->Type = Sketcher::Tangent;
            tangent1->First int SketchObject::fillet(int GeoId1, int GeoId2,
                         const Base::Vector3d& refPnt1, const Base::Vector3d& refPnt2,
                         double radius, int nofAngles, bool trim)
{
    if (GeoId1 < 0 || GeoId1 > getHighestCurveIndex() ||
        GeoId2 < 0 || GeoId2 > getHighestCurveIndex())
        return -1;

    const Part::Geometry *geo1 = getGeometry(GeoId1);
    const Part::Geometry *geo2 = getGeometry(GeoId2);
    Part::GeomArcOfCircle *arc;
    int filletId;
	bool FilletCreated = 0;
    if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
        geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId() ) {
        const Part::GeomLineSegment *lineSeg1 = static_cast<const Part::GeomLineSegment*>(geo1);
        const Part::GeomLineSegment *lineSeg2 = static_cast<const Part::GeomLineSegment*>(geo2);

        Base::Vector3d filletCenter;
        if (!Part::findFilletCenter(lineSeg1, lineSeg2, radius, refPnt1, refPnt2, filletCenter))
            return -1;
        Base::Vector3d dir1 = lineSeg1->getEndPoint() - lineSeg1->getStartPoint();
        Base::Vector3d dir2 = lineSeg2->getEndPoint() - lineSeg2->getStartPoint();

        // the intersection point will and two distances will be necessary later for trimming the lines
        Base::Vector3d intersection, dist1, dist2;

        // create arc from known parameters and lines
        *arc = Part::createFilletGeometry(lineSeg1, lineSeg2, filletCenter, radius);
        if (arc) {
            // calculate intersection and distances before we invalidate lineSeg1 and lineSeg2
            if (!find2DLinesIntersection(lineSeg1, lineSeg2, intersection)) {
                delete arc;
                return -1;
            }
            dist1.ProjectToLine(arc->getStartPoint(/*emulateCCW=*/true)-intersection, dir1);
            dist2.ProjectToLine(arc->getStartPoint(/*emulateCCW=*/true)-intersection, dir2);
            Part::Geometry *newgeo = arc;
            filletId = addGeometry(newgeo);
            if (filletId < 0= GeoId1;
            tangent1->FirstPos = PosId1;
            tangent1->Second = filletId;

            tangent2->Type = Sketcher::Tangent;
            tangent2->First = GeoId2;
            tangent2->FirstPos = PosId2;
            tangent2->Second = filletId;

            if (dist1.Length() < dist2.Length()) {
                tangent1->SecondPos = start;
                tangent2->SecondPos = end;
                movePoint(GeoId1, PosId1, arc->getStartPoint(/*emulateCCW=*/true),false,true);
                movePoint(GeoId2, PosId2, arc->getEndPoint(/*emulateCCW=*/true),false,true);
            }
            else {
                tangent1->SecondPos = end;
                tangent2->SecondPos = start;
                movePoint(GeoId1, PosId1, arc->getEndPoint(/*emulateCCW=*/true),false,true);
                movePoint(GeoId2, PosId2, arc->getStartPoint(/*emulateCCW=*/true),false,true);
            }

            addConstraint(tangent1);
            addConstraint(tangent2);
            delete tangent1;
            delete tangent2;
        }


        FilletCreated=1;
    }






    else if(geo1->isDerivedFrom(Part::GeomBoundedCurve::getClassTypeId())  &&
            geo2->isDerivedFrom(Part::GeomBoundedCurve::getClassTypeId())) 
//I removed this case for better readability on the forum as it's very long, and make similar things as above.




	//Let's add here our chamfer section. Above we make the fillet arc. Then here we can add the lines of chamfer if needed,
	// and arc will be put as construction back in GUI or here? Let's see what we can do.

	if(nofAngles != 1 && FilletCreated){ //if not normal fillet.

        Part::GeomArcOfCircle *arcToUse = *arc;
        //Case of inward fillet or chamfer. We need to make symmetric arc.
        if(nofAngles < -2 || nofAngles == -1){ //Inward chamfer (-2) is same as normal chamfer (2) so we don't need symmetric arc.
            //Create construction line between the extremities of fillet arc.
            Part::GeomLineSegment *arcLine = new Part::GeomLineSegment();
            arcLine->setPoints(arc->getStartPoint(/*emulateCCW=*/true),arc->getEndPoint(/*emulateCCW=*/true));
            arcLine->Construction=true;
            Part::Geometry *newgeo = arcLine;
            int arcLineId = addGeometry(newgeo);
            if (arcLineId < 0) {
                delete arcLine;
                delete arc;
                delete arcToUse;
                return -1;
            }

            //Create symetric arc (symArc) of arc by the arcLine.
            Part::GeomArcOfCircle *symArc = new Part::GeomArcOfCircle();
            symArc->setRadius(arc->getRadius()); //do we need to set values as we'll add constraint after?
?            Base::Vector3d symArcCenter;
?            symArc->setCenter(symArcCenter);
?            double startAngle, endAngle;
?            arc->getRange(startAngle, endAngle, /*emulateCCWXY=*/true);
?            symArc->setRange(startAngle+M_PI, endAngle+M_PI, /*emulateCCWXY=*/true);//do we need to set values as we'll add constraint after?

            // add symmetric arc to sketch geometry
            Part::Geometry *newgeo2 = symArc;
            int symFilletId = addGeometry(newgeo2);
            if (symFilletId < 0) {
                delete arcLine;
                delete arc;
                delete symArc;
                delete arcToUse;
                return -1;
            }

            //Constraint 1 : center of arc and center of symArc symmetric by arcLine
            Sketcher::Constraint *sym1 = new Sketcher::Constraint();
            sym1->Type = Sketcher::Symmetric; //symmetric is two point and a line. So we need to use center of arcs and arcLine.
?            sym1->First = ???;
?            sym1->FirstPos = ???;
?            sym1->Second = ???;
            addConstraint(sym1);
            delete sym1;

            //Constraint 2&3 : coincidence of arc and symArc start and end.
            Sketcher::Constraint *newConstr = new Sketcher::Constraint();
            newConstr->Type = Sketcher::Coincident;
            newConstr->First = filletId;
            newConstr->FirstPos = start;
            newConstr->Second = symFilletId;
            newConstr->SecondPos = end;
            addConstraint(newConstr);

            newConstr->Type = Sketcher::Coincident;
            newConstr->First = filletId;
            newConstr->FirstPos = end;
            newConstr->Second = symFilletId;
            newConstr->SecondPos = start;
            addConstraint(newConstr);
            delete newConstr;

            //Put original arc as construction.
?            arc->Construction=true; //if we change arc will it change filletID?

            //The arc to use for poly-chamfer will be the symmetric arc.
            arcToUse = symArc;

        }


        //case of chamfer or poly-chamfer. Both normal and inward should be treated the same. Only the arc changes.
        int absNofAngles = abs(nofAngles);
        if(absNofAngles > 1){
            //Put arcToUse as construction
            arcToUse->Construction=true;

            //We need to create the points with the angular difference, then we'll create lines, then the constraints.
            //We adapt the logic of the regular polygon (Mod\Sketcher\ProfileLib\regularPolygon.py).

            //Get the total angle of the fillet :
            double startAngle, endAngle, filletAngle, angular_diff;
            arcToUse->getRange(startAngle, endAngle, /*emulateCCWXY=*/true);
            filletAngle = endAngle-startAngle;
            angular_diff = filletAngle/(nofAngles-1); //number of side = nofAngles-1


            //Create a list of the points
            Base::Vector3d diffVec = arcToUse->getStartPoint(/*emulateCCW=*/true) - arcToUse->getCenter()
            std vector <Base::Vector3d> listOfPoints;
            listOfPoints.push_back(arcToUse->getStartPoint(/*emulateCCW=*/true));
            for(int i = 1; i < nofAngles-1; i++){
                double cos_v = math.cos( angular_diff * i );
                double sin_v = math.sin( angular_diff * i );
?                Base::Vector3d pointToAdd = centerPoint + (cos_v * diffVec.x - sin_v * diffVec.y,
                                                           cos_v * diffVec.y + sin_v * diffVec.x,
                                                           0 );
                listOfPoints.push_back(pointToAdd);

            }
            listOfPoints.push_back(arcToUse->getEndPoint(/*emulateCCW=*/true));

            //create lines from points.
            std::vector<int> listOfLines;
            for(int i = 0; i < listOfPoints.size()-1; i++){
                Part::GeomLineSegment *linei = new Part::GeomLineSegment();
                linei->setPoints(listOfPoints[i],listOfPoints[i+1]);

                Part::Geometry *newgeo = linei;
                int lineID = addGeometry(newgeo);
                if (lineID < 0) {
                    delete arcLine;
                    delete arc;
                    delete linei;
                    return -1;
                }
                listOfLines.push_back(lineID);
                delete linei;
            }

            //Create constraints.  0- start of first line with the arc start
            Sketcher::Constraint *newConstr = new Sketcher::Constraint();
            newConstr->Type = Sketcher::Coincident;
            newConstr->First = filletId;
            newConstr->FirstPos = start;
            newConstr->Second = listOfLines[0];
            newConstr->SecondPos = start;
            addConstraint(newConstr);

            for(int i = 0; i < listOfLines.size()-2; i++){
                //1- constraints of coincident of one line end with next line start
                newConstr->Type = Sketcher::Coincident;
                newConstr->First = listOfLines[i];
                newConstr->FirstPos = end;
                newConstr->Second = listOfLines[i+1];
                newConstr->SecondPos = start;
                addConstraint(newConstr);

                //2- Coincidence of line end with arc.
                newConstr->Type = Sketcher::Coincident;
                newConstr->First = listOfLines[i];
                newConstr->FirstPos = end;
                newConstr->Second = filletId;
                addConstraint(newConstr);

                //3-constraint of equal of line with next line. So in the end all lines are equal together.
                newConstr->Type = Sketcher::Equal;
                newConstr->First = listOfLines[i];
                newConstr->Second = listOfLines[i+1];
                addConstraint(newConstr);
            }
            //4 - End of last line with arc end.
            newConstr->Type = Sketcher::Coincident;
            newConstr->First = listOfLines[listOfLines.size()-1];
            newConstr->FirstPos = end;
            newConstr->Second = filletId;
            newConstr->SecondPos = end;
            addConstraint(newConstr);
            delete newConstr;
        }
	}

	if(FilletCreated){
        delete arc;
            if(nofAngles != 1 && FilletCreated){
                delete arcToUse;
                if(nofAngles < -2 || nofAngles == -1){
                    delete symArc;
                }
            }

        if (noRecomputes) // if we do not have a recompute after the geometry creation, the sketch must be solved to update the DoF of the solver
            solve();

		return 0;
	}

    return -1;
}
}
Last edited by paddle on Fri Mar 06, 2020 4:59 pm, edited 4 times in total.
User avatar
Kunda1
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: Feature development : chamfer on sketch tool and complex fillet

Post by Kunda1 »

Awesome, do you have a public repo like on GitHub you can add to the OP?
Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
chrisb
Veteran
Posts: 54150
Joined: Tue Mar 17, 2015 9:14 am

Re: Feature development : chamfer on sketch tool and complex fillet

Post by chrisb »

Moved from Part Design module development forum.
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
User avatar
paddle
Veteran
Posts: 1412
Joined: Mon Feb 03, 2020 4:47 pm

Re: Feature development : chamfer on sketch tool and complex fillet

Post by paddle »

Kunda1 wrote: Wed Feb 26, 2020 5:28 pm Awesome, do you have a public repo like on GitHub you can add to the OP?
Currently I made a git clone on my local harddrive and I was planning to make a pull request when it's working. But I'm not sure it's the correct way of doing.
Maybe I should make a fork on github?
User avatar
Kunda1
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: Feature development : chamfer on sketch tool and complex fillet

Post by Kunda1 »

paddle wrote: Thu Feb 27, 2020 2:26 pm
Kunda1 wrote: Wed Feb 26, 2020 5:28 pm Awesome, do you have a public repo like on GitHub you can add to the OP?
Currently I made a git clone on my local harddrive and I was planning to make a pull request when it's working. But I'm not sure it's the correct way of doing.
Maybe I should make a fork on github?
Yea, we have a page about this on the wiki called Github AKA source code management
It explains step by step how to use github in the workflow and that way people can actually review and/or send PRs to your repository for patches before you submit your PR

Edit: if you have any issues, just let the community here know and we can orient you on how to get more savvy with git. Thank you for your work
Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
User avatar
paddle
Veteran
Posts: 1412
Joined: Mon Feb 03, 2020 4:47 pm

Re: Feature development : chamfer on sketch tool and complex fillet

Post by paddle »

Kunda1 wrote: Thu Feb 27, 2020 2:31 pm Yea, we have a page about this on the wiki called Github AKA source code management
It explains step by step how to use github in the workflow and that way people can actually review and/or send PRs to your repository for patches before you submit your PR

Edit: if you have any issues, just let the community here know and we can orient you on how to get more savvy with git. Thank you for your work
I'm new to git so I think I missed this.

I made my questions on the first post clearer. So if someone can help with them we can get this working just after I think.
User avatar
paddle
Veteran
Posts: 1412
Joined: Mon Feb 03, 2020 4:47 pm

Re: Feature development : chamfer on sketch tool and complex fillet

Post by paddle »

Additional question :
What is the syntax to add points together with different x,y,z ?
I'm adapting the logic of regular polygon, but it's writen in python so syntax is not the same.

Code: Select all

            //Create a list of the points
            Base::Vector3d diffVec = arcToUse->getStartPoint(/*emulateCCW=*/true) - arcToUse->getCenter() 
            std vector <Base::Vector3d> listOfPoints;
            listOfPoints.push_back(arcToUse->getStartPoint(/*emulateCCW=*/true));
            for(int i = 1; i < nofAngles-1; i++){
                double cos_v = math.cos( angular_diff * i );
                double sin_v = math.sin( angular_diff * i );
?                Base::Vector3d pointToAdd = centerPoint + (cos_v * diffVec.x - sin_v * diffVec.y,
                                                           cos_v * diffVec.y + sin_v * diffVec.x,
                                                           0 );
                listOfPoints.push_back(pointToAdd);

            }
User avatar
Kunda1
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: Feature development : chamfer on sketch tool and complex fillet

Post by Kunda1 »

Chris_G wrote::bell:
Hey @Chris_G do you mind weighing in here? Thanks!
Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
User avatar
paddle
Veteran
Posts: 1412
Joined: Mon Feb 03, 2020 4:47 pm

Re: Feature development : chamfer on sketch tool and complex fillet

Post by paddle »

Anyone? :)
I'd like to go further than that and I have plenty of ideas like round square rectangles/polygons, spiral, cogs and lot more.
Though to go onward I need some help there !
User avatar
Kunda1
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: Feature development : chamfer on sketch tool and complex fillet

Post by Kunda1 »

Stay persistent, this isn't the best time to actually implement new features because we're right on the cusp of a feature freeze. Nevertheless it's important to have things in the pipeline as well. ;)
Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
Post Reply