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.
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);
}
- 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;
- 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?
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;
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;
}
}