Sketcher: Chamfer tool (and fillet refactor)

This commit is contained in:
PaddleStroke
2024-03-12 11:45:08 +01:00
committed by WandererFan
parent 9d43e7fda7
commit b3fe5bba28
12 changed files with 1342 additions and 420 deletions

View File

@@ -548,7 +548,7 @@
<FCText Name="Sketcher_CreatePeriodicBSpline">G, B, P</FCText>
<FCText Name="Sketcher_CreatePeriodicBSplineByInterpolation">G, B, O</FCText>
<FCText Name="Sketcher_CreatePoint">G, Y</FCText>
<FCText Name="Sketcher_CreatePointFillet">G, F, P</FCText>
<FCText Name="Sketcher_CreateChamfer">G, F, C</FCText>
<FCText Name="Sketcher_CreatePolyline">G, M</FCText>
<FCText Name="Sketcher_CreateRectangle">G, R</FCText>
<FCText Name="Sketcher_CreateRectangle_Center">G, V</FCText>

View File

@@ -2065,7 +2065,7 @@ int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toG
return 0;
}
int SketchObject::fillet(int GeoId, PointPos PosId, double radius, bool trim, bool createCorner)
int SketchObject::fillet(int GeoId, PointPos PosId, double radius, bool trim, bool createCorner, bool chamfer)
{
if (GeoId < 0 || GeoId > getHighestCurveIndex())
return -1;
@@ -2081,12 +2081,12 @@ int SketchObject::fillet(int GeoId, PointPos PosId, double radius, bool trim, bo
const Part::Geometry* geo2 = getGeometry(GeoIdList[1]);
if (geo1->is<Part::GeomLineSegment>()
&& geo2->is<Part::GeomLineSegment>()) {
const Part::GeomLineSegment* lineSeg1 = static_cast<const Part::GeomLineSegment*>(geo1);
const Part::GeomLineSegment* lineSeg2 = static_cast<const Part::GeomLineSegment*>(geo2);
auto* lineSeg1 = static_cast<const Part::GeomLineSegment*>(geo1);
auto* lineSeg2 = static_cast<const Part::GeomLineSegment*>(geo2);
Base::Vector3d midPnt1 = (lineSeg1->getStartPoint() + lineSeg1->getEndPoint()) / 2;
Base::Vector3d midPnt2 = (lineSeg2->getStartPoint() + lineSeg2->getEndPoint()) / 2;
return fillet(GeoIdList[0], GeoIdList[1], midPnt1, midPnt2, radius, trim, createCorner);
return fillet(GeoIdList[0], GeoIdList[1], midPnt1, midPnt2, radius, trim, createCorner, chamfer);
}
}
@@ -2094,11 +2094,11 @@ int SketchObject::fillet(int GeoId, PointPos PosId, double radius, bool trim, bo
}
int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
const Base::Vector3d& refPnt2, double radius, bool trim, bool createCorner)
const Base::Vector3d& refPnt2, double radius, bool trim, bool createCorner, bool chamfer)
{
if (GeoId1 < 0 || GeoId1 > getHighestCurveIndex() || GeoId2 < 0
|| GeoId2 > getHighestCurveIndex())
if (GeoId1 < 0 || GeoId1 > getHighestCurveIndex() || GeoId2 < 0 || GeoId2 > getHighestCurveIndex()) {
return -1;
}
// If either of the two input lines are locked, don't try to trim since it won't work anyway
const Part::Geometry* geo1 = getGeometry(GeoId1);
@@ -2107,14 +2107,19 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
trim = false;
}
if (geo1->is<Part::GeomLineSegment>()
&& geo2->is<Part::GeomLineSegment>()) {
const Part::GeomLineSegment* lineSeg1 = static_cast<const Part::GeomLineSegment*>(geo1);
const Part::GeomLineSegment* lineSeg2 = static_cast<const Part::GeomLineSegment*>(geo2);
Base::Vector3d p1, p2;
PointPos PosId1 = PointPos::none;
PointPos PosId2 = PointPos::none;
int filletId;
if (geo1->is<Part::GeomLineSegment>() && geo2->is<Part::GeomLineSegment>()) {
auto* lineSeg1 = static_cast<const Part::GeomLineSegment*>(geo1);
auto* lineSeg2 = static_cast<const Part::GeomLineSegment*>(geo2);
Base::Vector3d filletCenter;
if (!Part::findFilletCenter(lineSeg1, lineSeg2, radius, refPnt1, refPnt2, 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();
@@ -2123,27 +2128,27 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
Base::Vector3d intersection, dist1, dist2;
// create arc from known parameters and lines
int filletId;
std::unique_ptr<Part::GeomArcOfCircle> arc(
Part::createFilletGeometry(lineSeg1, lineSeg2, filletCenter, radius));
if (!arc)
if (!arc) {
return -1;
}
// calculate intersection and distances before we invalidate lineSeg1 and lineSeg2
if (!find2DLinesIntersection(lineSeg1, lineSeg2, intersection)) {
return -1;
}
p1 = arc->getStartPoint();
p2 = arc->getEndPoint();
dist1.ProjectToLine(arc->getStartPoint(/*emulateCCW=*/true) - intersection, dir1);
dist2.ProjectToLine(arc->getStartPoint(/*emulateCCW=*/true) - intersection, dir2);
Part::Geometry* newgeo = arc.get();
filletId = addGeometry(newgeo);
filletId = addGeometry(arc.get());
if (trim) {
PointPos PosId1 =
(filletCenter - intersection) * dir1 > 0 ? PointPos::start : PointPos::end;
PointPos PosId2 =
(filletCenter - intersection) * dir2 > 0 ? PointPos::start : PointPos::end;
PosId1 = (filletCenter - intersection) * dir1 > 0 ? PointPos::start : PointPos::end;
PosId2 = (filletCenter - intersection) * dir2 > 0 ? PointPos::start : PointPos::end;
if (createCorner) {
transferFilletConstraints(GeoId1, PosId1, GeoId2, PosId2);
@@ -2182,16 +2187,8 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
addConstraint(std::move(tangent1));
addConstraint(std::move(tangent2));
}
// if we do not have a recompute after the geometry creation, the sketch must be solved to
// update the DoF of the solver
if (noRecomputes)
solve();
return 0;
}
else if (geo1->isDerivedFrom<Part::GeomBoundedCurve>()
&& geo2->isDerivedFrom<Part::GeomBoundedCurve>()) {
else if (geo1->isDerivedFrom<Part::GeomBoundedCurve>() && geo2->isDerivedFrom<Part::GeomBoundedCurve>()) {
auto distancetorefpoints =
[](Base::Vector3d ip1, Base::Vector3d ip2, Base::Vector3d ref1, Base::Vector3d ref2) {
@@ -2284,40 +2281,35 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
// look for coincident constraints between curves, take the coincident closest to the
// refpoints
Sketcher::PointPos curve1PosId = Sketcher::PointPos::none;
Sketcher::PointPos curve2PosId = Sketcher::PointPos::none;
double dist = INFINITY;
const std::vector<Constraint*>& constraints = this->Constraints.getValues();
for (std::vector<Constraint*>::const_iterator it = constraints.begin();
it != constraints.end();
++it) {
if ((*it)->Type == Sketcher::Coincident || (*it)->Type == Sketcher::Perpendicular
|| (*it)->Type == Sketcher::Tangent) {
if ((*it)->First == GeoId1 && (*it)->Second == GeoId2
&& (*it)->FirstPos != Sketcher::PointPos::none
&& (*it)->SecondPos != Sketcher::PointPos::none) {
Base::Vector3d tmpp1 = getPoint((*it)->First, (*it)->FirstPos);
Base::Vector3d tmpp2 = getPoint((*it)->Second, (*it)->SecondPos);
for (auto& constr : constraints) {
if (constr->Type == Sketcher::Coincident || constr->Type == Sketcher::Perpendicular
|| constr->Type == Sketcher::Tangent) {
if (constr->First == GeoId1 && constr->Second == GeoId2
&& constr->FirstPos != PointPos::none
&& constr->SecondPos != PointPos::none) {
Base::Vector3d tmpp1 = getPoint(constr->First, constr->FirstPos);
Base::Vector3d tmpp2 = getPoint(constr->Second, constr->SecondPos);
double tmpdist = distancetorefpoints(tmpp1, tmpp2, refPnt1, refPnt2);
if (tmpdist < dist) {
curve1PosId = (*it)->FirstPos;
curve2PosId = (*it)->SecondPos;
PosId1 = constr->FirstPos;
PosId2 = constr->SecondPos;
dist = tmpdist;
interpoints = std::make_pair(tmpp1, tmpp2);
}
}
else if ((*it)->First == GeoId2 && (*it)->Second == GeoId1
&& (*it)->FirstPos != Sketcher::PointPos::none
&& (*it)->SecondPos != Sketcher::PointPos::none) {
Base::Vector3d tmpp2 = getPoint((*it)->First, (*it)->FirstPos);
Base::Vector3d tmpp1 = getPoint((*it)->Second, (*it)->SecondPos);
else if (constr->First == GeoId2 && constr->Second == GeoId1
&& constr->FirstPos != PointPos::none
&& constr->SecondPos != PointPos::none) {
Base::Vector3d tmpp2 = getPoint(constr->First, constr->FirstPos);
Base::Vector3d tmpp1 = getPoint(constr->Second, constr->SecondPos);
double tmpdist = distancetorefpoints(tmpp1, tmpp2, refPnt1, refPnt2);
if (tmpdist < dist) {
curve2PosId = (*it)->FirstPos;
curve1PosId = (*it)->SecondPos;
PosId2 = constr->FirstPos;
PosId1 = constr->SecondPos;
dist = tmpdist;
interpoints = std::make_pair(tmpp1, tmpp2);
}
@@ -2325,15 +2317,13 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
}
}
if (curve1PosId == Sketcher::PointPos::none) {
if (PosId1 == PointPos::none) {
// no coincident was found, try basis curve intersection if GeomTrimmedCurve
if (geo1->isDerivedFrom(Part::GeomTrimmedCurve::getClassTypeId())
&& geo2->isDerivedFrom(Part::GeomTrimmedCurve::getClassTypeId())) {
if (geo1->isDerivedFrom<Part::GeomTrimmedCurve>()
&& geo2->isDerivedFrom<Part::GeomTrimmedCurve>()) {
const Part::GeomTrimmedCurve* tcurve1 =
static_cast<const Part::GeomTrimmedCurve*>(geo1);
const Part::GeomTrimmedCurve* tcurve2 =
static_cast<const Part::GeomTrimmedCurve*>(geo2);
auto* tcurve1 =static_cast<const Part::GeomTrimmedCurve*>(geo1);
auto* tcurve2 = static_cast<const Part::GeomTrimmedCurve*>(geo2);
try {
if (!tcurve1->intersectBasisCurves(tcurve2, points))
@@ -2350,11 +2340,13 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
int res = selectintersection(points, interpoints, refPnt1, refPnt2);
if (res != 0)
if (res != 0) {
return res;
}
}
else
else {
return -1;// not a GeomTrimmedCurve and no coincident point.
}
}
// Now that we know where the curves intersect, get the parameters in the curves of those
@@ -2363,8 +2355,9 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
double intparam2;
try {
if (!curve1->closestParameter(interpoints.first, intparam1))
if (!curve1->closestParameter(interpoints.first, intparam1)) {
return -1;
}
}
catch (Base::CADKernelError& e) {
e.ReportException();
@@ -2374,8 +2367,9 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
}
try {
if (!curve2->closestParameter(interpoints.second, intparam2))
if (!curve2->closestParameter(interpoints.second, intparam2)) {
return -1;
}
}
catch (Base::CADKernelError& e) {
e.ReportException();
@@ -2439,8 +2433,7 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
/ det,
0);
radius =
((refp1 - normalintersect).Length() + (refp2 - normalintersect).Length()) / 2;
radius = ((refp1 - normalintersect).Length() + (refp2 - normalintersect).Length()) / 2;
}
catch (const Base::Exception&) {
radius = ref21.Length();// fall-back to simplest estimation.
@@ -2543,8 +2536,9 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
int res = selectintersection(offsetintersectionpoints, filletcenterpoint, refPnt1, refPnt2);
if (res != 0)
if (res != 0) {
return res;
}
#ifdef DEBUG
Base::Console().Log(
@@ -2555,8 +2549,9 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
double refoparam2;
try {
if (!curve1->closestParameter(filletcenterpoint.first, refoparam1))
if (!curve1->closestParameter(filletcenterpoint.first, refoparam1)) {
return -1;
}
}
catch (Base::CADKernelError& e) {
e.ReportException();
@@ -2564,8 +2559,9 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
}
try {
if (!curve2->closestParameter(filletcenterpoint.second, refoparam2))
if (!curve2->closestParameter(filletcenterpoint.second, refoparam2)) {
return -1;
}
}
catch (Base::CADKernelError& e) {
e.ReportException();
@@ -2599,25 +2595,29 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
endAngle = startAngle + range;
if (endAngle < startAngle)
if (endAngle < startAngle) {
std::swap(startAngle, endAngle);
}
if (endAngle > 2 * M_PI)
if (endAngle > 2 * M_PI) {
endAngle -= 2 * M_PI;
}
if (startAngle < 0)
if (startAngle < 0) {
endAngle += 2 * M_PI;
}
// Create Arc Segment
Part::GeomArcOfCircle* arc = new Part::GeomArcOfCircle();
auto* arc = new Part::GeomArcOfCircle();
arc->setRadius(radDir1.Length());
arc->setCenter(filletcenterpoint.first);
arc->setRange(startAngle, endAngle, /*emulateCCWXY=*/true);
p1 = arc->getStartPoint();
p2 = arc->getEndPoint();
// add arc to sketch geometry
int filletId;
Part::Geometry* newgeo = arc;
filletId = addGeometry(newgeo);
filletId = addGeometry(arc);
if (filletId < 0) {
delete arc;
return -1;
@@ -2639,27 +2639,27 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
// b) we used the basis curve intersection
if (curve1PosId == Sketcher::PointPos::none) {
curve1PosId = selectend(intparam1, refoparam1, spc1);
curve2PosId = selectend(intparam2, refoparam2, spc2);
if (PosId1 == Sketcher::PointPos::none) {
PosId1 = selectend(intparam1, refoparam1, spc1);
PosId2 = selectend(intparam2, refoparam2, spc2);
}
delConstraintOnPoint(GeoId1, curve1PosId, false);
delConstraintOnPoint(GeoId2, curve2PosId, false);
delConstraintOnPoint(GeoId1, PosId1, false);
delConstraintOnPoint(GeoId2, PosId2, false);
Sketcher::Constraint* tangent1 = new Sketcher::Constraint();
Sketcher::Constraint* tangent2 = new Sketcher::Constraint();
auto* tangent1 = new Sketcher::Constraint();
auto* tangent2 = new Sketcher::Constraint();
tangent1->Type = Sketcher::Tangent;
tangent1->First = GeoId1;
tangent1->FirstPos = curve1PosId;
tangent1->FirstPos = PosId1;
tangent1->Second = filletId;
tangent2->Type = Sketcher::Tangent;
tangent2->First = GeoId2;
tangent2->FirstPos = curve2PosId;
tangent2->FirstPos = PosId2;
tangent2->Second = filletId;
double dist1 = (refp1 - arc->getStartPoint(true)).Length();
@@ -2670,14 +2670,14 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
if (dist1 < dist2) {
tangent1->SecondPos = PointPos::start;
tangent2->SecondPos = PointPos::end;
movePoint(GeoId1, curve1PosId, arc->getStartPoint(true), false, true);
movePoint(GeoId2, curve2PosId, arc->getEndPoint(true), false, true);
movePoint(GeoId1, PosId1, arc->getStartPoint(true), false, true);
movePoint(GeoId2, PosId2, arc->getEndPoint(true), false, true);
}
else {
tangent1->SecondPos = PointPos::end;
tangent2->SecondPos = PointPos::start;
movePoint(GeoId1, curve1PosId, arc->getEndPoint(true), false, true);
movePoint(GeoId2, curve2PosId, arc->getStartPoint(true), false, true);
movePoint(GeoId1, PosId1, arc->getEndPoint(true), false, true);
movePoint(GeoId2, PosId2, arc->getStartPoint(true), false, true);
}
addConstraint(tangent1);
@@ -2692,15 +2692,53 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1,
#ifdef DEBUG
Base::Console().Log("\n\nEND OF FILLET DEBUG\n\n");
#endif
// if we do not have a recompute after the geometry creation, the sketch must be solved to
// update the DoF of the solver
if (noRecomputes)
solve();
return 0;
}
return -1;
else {
return -1;
}
if (chamfer) {
auto line = std::make_unique<Part::GeomLineSegment>();
line->setPoints(p1, p2);
int lineGeoId = addGeometry(line.get());
auto coinc1 = std::make_unique<Sketcher::Constraint>();
auto coinc2 = std::make_unique<Sketcher::Constraint>();
coinc1->Type = Sketcher::Coincident;
coinc1->First = lineGeoId;
coinc1->FirstPos = PointPos::start;
coinc2->Type = Sketcher::Coincident;
coinc2->First = lineGeoId;
coinc2->FirstPos = PointPos::end;
if (trim) {
coinc1->Second = GeoId1;
coinc1->SecondPos = PosId1;
coinc2->Second = GeoId2;
coinc2->SecondPos = PosId2;
}
else {
coinc1->Second = filletId;
coinc1->SecondPos = PointPos::start;
coinc2->Second = filletId;
coinc2->SecondPos = PointPos::end;
}
addConstraint(std::move(coinc1));
addConstraint(std::move(coinc2));
setConstruction(filletId, true);
}
// if we do not have a recompute after the geometry creation, the sketch must be solved to
// update the DoF of the solver
if (noRecomputes) {
solve();
}
return 0;
}
int SketchObject::extend(int GeoId, double increment, PointPos endpoint)

View File

@@ -323,8 +323,12 @@ public:
\param createCorner - keep geoId/pos as a Point and keep as many constraints as possible
\retval - 0 on success, -1 on failure
*/
int
fillet(int geoId, PointPos pos, double radius, bool trim = true, bool preserveCorner = false);
int fillet(int geoId,
PointPos pos,
double radius,
bool trim = true,
bool preserveCorner = false,
bool chamfer = false);
/*!
\brief More general form of fillet
\param geoId1, geoId2 - geoId for two lines (which don't necessarily have to coincide)
@@ -340,7 +344,8 @@ public:
const Base::Vector3d& refPnt2,
double radius,
bool trim = true,
bool createCorner = false);
bool createCorner = false,
bool chamfer = false);
/// trim a curve
int trim(int geoId, const Base::Vector3d& point);

View File

@@ -1196,11 +1196,12 @@ PyObject* SketchObjectPy::fillet(PyObject* args)
int geoId1, geoId2, posId1;
int trim = true;
PyObject* createCorner = Py_False;
PyObject* chamfer = Py_False;
double radius;
// Two Lines, radius
if (PyArg_ParseTuple(args,
"iiO!O!d|iO!",
"iiO!O!d|iO!O!",
&geoId1,
&geoId2,
&(Base::VectorPy::Type),
@@ -1210,15 +1211,23 @@ PyObject* SketchObjectPy::fillet(PyObject* args)
&radius,
&trim,
&PyBool_Type,
&createCorner)) {
&createCorner,
&PyBool_Type,
&chamfer)) {
// The i for &trim should probably have been a bool like &createCorner, but we'll leave it
// an int for backward compatibility (and because python will accept a bool there anyway)
Base::Vector3d v1 = static_cast<Base::VectorPy*>(pcObj1)->value();
Base::Vector3d v2 = static_cast<Base::VectorPy*>(pcObj2)->value();
if (this->getSketchObjectPtr()
->fillet(geoId1, geoId2, v1, v2, radius, trim, Base::asBoolean(createCorner))) {
if (this->getSketchObjectPtr()->fillet(geoId1,
geoId2,
v1,
v2,
radius,
trim,
Base::asBoolean(createCorner),
Base::asBoolean(chamfer))) {
std::stringstream str;
str << "Not able to fillet curves with ids : (" << geoId1 << ", " << geoId2
<< ") and points (" << v1.x << ", " << v1.y << ", " << v1.z << ") & "
@@ -1232,18 +1241,21 @@ PyObject* SketchObjectPy::fillet(PyObject* args)
PyErr_Clear();
// Point, radius
if (PyArg_ParseTuple(args,
"iid|iO!",
"iid|iO!O!",
&geoId1,
&posId1,
&radius,
&trim,
&PyBool_Type,
&createCorner)) {
&createCorner,
&PyBool_Type,
&chamfer)) {
if (this->getSketchObjectPtr()->fillet(geoId1,
static_cast<Sketcher::PointPos>(posId1),
radius,
trim,
Base::asBoolean(createCorner))) {
Base::asBoolean(createCorner),
Base::asBoolean(chamfer))) {
std::stringstream str;
str << "Not able to fillet point with ( geoId: " << geoId1 << ", PointPos: " << posId1
<< " )";

View File

@@ -1431,8 +1431,9 @@ CmdSketcherCreateFillet::CmdSketcherCreateFillet()
void CmdSketcherCreateFillet::activated(int iMsg)
{
Q_UNUSED(iMsg);
ActivateHandler(getActiveGuiDocument(),
new DrawSketchHandlerFillet(DrawSketchHandlerFillet::SimpleFillet));
ActivateHandler(
getActiveGuiDocument(),
new DrawSketchHandlerFillet(ConstructionMethods::FilletConstructionMethod::Fillet));
}
bool CmdSketcherCreateFillet::isActive()
@@ -1442,149 +1443,62 @@ bool CmdSketcherCreateFillet::isActive()
// ======================================================================================
DEF_STD_CMD_A(CmdSketcherCreatePointFillet)
DEF_STD_CMD_A(CmdSketcherCreateChamfer)
CmdSketcherCreatePointFillet::CmdSketcherCreatePointFillet()
: Command("Sketcher_CreatePointFillet")
CmdSketcherCreateChamfer::CmdSketcherCreateChamfer()
: Command("Sketcher_CreateChamfer")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Create corner-preserving fillet");
sToolTipText = QT_TR_NOOP("Fillet that preserves intersection point and most constraints");
sWhatsThis = "Sketcher_CreatePointFillet";
sMenuText = QT_TR_NOOP("Create chamfer");
sToolTipText = QT_TR_NOOP("Create a chamfer between two lines or at a coincident point");
sWhatsThis = "Sketcher_CreateChamfer";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_CreatePointFillet";
sAccel = "G, F, P";
sPixmap = "Sketcher_CreateChamfer";
sAccel = "G, F, C";
eType = ForEdit;
}
void CmdSketcherCreatePointFillet::activated(int iMsg)
void CmdSketcherCreateChamfer::activated(int iMsg)
{
Q_UNUSED(iMsg);
ActivateHandler(
getActiveGuiDocument(),
new DrawSketchHandlerFillet(DrawSketchHandlerFillet::ConstraintPreservingFillet));
new DrawSketchHandlerFillet(ConstructionMethods::FilletConstructionMethod::Chamfer));
}
bool CmdSketcherCreatePointFillet::isActive()
bool CmdSketcherCreateChamfer::isActive()
{
return isCommandActive(getActiveGuiDocument());
}
/// @brief Macro that declares a new sketcher command class 'CmdSketcherCompCreateFillets'
DEF_STD_CMD_ACLU(CmdSketcherCompCreateFillets)
/**
* @brief ctor
*/
CmdSketcherCompCreateFillets::CmdSketcherCompCreateFillets()
: Command("Sketcher_CompCreateFillets")
class CmdSketcherCompCreateFillets: public Gui::GroupCommand
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Create fillet");
sToolTipText = QT_TR_NOOP("Create a fillet between two lines");
sWhatsThis = "Sketcher_CompCreateFillets";
sStatusTip = sToolTipText;
eType = ForEdit;
}
public:
CmdSketcherCompCreateFillets()
: GroupCommand("Sketcher_CompCreateFillets")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Create fillet or chamfer");
sToolTipText = QT_TR_NOOP("Create a fillet or chamfer between two lines");
sWhatsThis = "Sketcher_CompCreateFillets";
sStatusTip = sToolTipText;
eType = ForEdit;
/**
* @brief Instantiates the fillet handler when the fillet command activated
* @param int iMsg
*/
void CmdSketcherCompCreateFillets::activated(int iMsg)
{
if (iMsg == 0) {
ActivateHandler(getActiveGuiDocument(),
new DrawSketchHandlerFillet(DrawSketchHandlerFillet::SimpleFillet));
}
else if (iMsg == 1) {
ActivateHandler(
getActiveGuiDocument(),
new DrawSketchHandlerFillet(DrawSketchHandlerFillet::ConstraintPreservingFillet));
}
else {
return;
setCheckable(false);
addCommand("Sketcher_CreateFillet");
addCommand("Sketcher_CreateChamfer");
}
// Since the default icon is reset when enabling/disabling the command we have
// to explicitly set the icon of the used command.
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
QList<QAction*> a = pcAction->actions();
assert(iMsg < a.size());
pcAction->setIcon(a[iMsg]->icon());
}
Gui::Action* CmdSketcherCompCreateFillets::createAction()
{
Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
pcAction->setDropDownMenu(true);
applyCommandData(this->className(), pcAction);
QAction* oldFillet = pcAction->addAction(QString());
oldFillet->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateFillet"));
QAction* pointFillet = pcAction->addAction(QString());
pointFillet->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePointFillet"));
_pcAction = pcAction;
languageChange();
pcAction->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePointFillet"));
int defaultId = 1;
pcAction->setProperty("defaultAction", QVariant(defaultId));
return pcAction;
}
void CmdSketcherCompCreateFillets::updateAction(int mode)
{
Q_UNUSED(mode);
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(getAction());
if (!pcAction) {
return;
const char* className() const override
{
return "CmdSketcherCompCreateFillets";
}
};
QList<QAction*> a = pcAction->actions();
int index = pcAction->property("defaultAction").toInt();
a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateFillet"));
a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePointFillet"));
getAction()->setIcon(a[index]->icon());
}
void CmdSketcherCompCreateFillets::languageChange()
{
Command::languageChange();
if (!_pcAction) {
return;
}
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
QList<QAction*> a = pcAction->actions();
QAction* oldFillet = a[0];
oldFillet->setText(QApplication::translate("CmdSketcherCompCreateFillets", "Sketch fillet"));
oldFillet->setToolTip(
QApplication::translate("Sketcher_CreateFillet", "Creates a radius between two lines"));
oldFillet->setStatusTip(
QApplication::translate("Sketcher_CreateFillet", "Creates a radius between two lines"));
QAction* pointFillet = a[1];
pointFillet->setText(
QApplication::translate("CmdSketcherCompCreateFillets", "Corner-preserving sketch fillet"));
pointFillet->setToolTip(
QApplication::translate("Sketcher_CreatePointFillet",
"Fillet that preserves constraints and intersection point"));
pointFillet->setStatusTip(
QApplication::translate("Sketcher_CreatePointFillet",
"Fillet that preserves constraints and intersection point"));
}
bool CmdSketcherCompCreateFillets::isActive()
{
return isCommandActive(getActiveGuiDocument());
}
// ======================================================================================
@@ -2323,9 +2237,9 @@ void CreateSketcherCommandsCreateGeo()
rcCmdMgr.addCommand(new CmdSketcherCreateSlot());
rcCmdMgr.addCommand(new CmdSketcherCreateArcSlot());
rcCmdMgr.addCommand(new CmdSketcherCompSlot());
rcCmdMgr.addCommand(new CmdSketcherCompCreateFillets());
rcCmdMgr.addCommand(new CmdSketcherCreateFillet());
rcCmdMgr.addCommand(new CmdSketcherCreatePointFillet());
rcCmdMgr.addCommand(new CmdSketcherCreateChamfer());
rcCmdMgr.addCommand(new CmdSketcherCompCreateFillets());
// rcCmdMgr.addCommand(new CmdSketcherCreateText());
// rcCmdMgr.addCommand(new CmdSketcherCreateDraftLine());
rcCmdMgr.addCommand(new CmdSketcherTrimming());

View File

@@ -35,6 +35,8 @@
#include "Utils.h"
#include "ViewProviderSketch.h"
using namespace Sketcher;
namespace SketcherGui
{
@@ -86,80 +88,106 @@ public:
};
class DrawSketchHandlerFillet: public DrawSketchHandler
{
public:
enum FilletType
{
SimpleFillet,
ConstraintPreservingFillet
};
class DrawSketchHandlerFillet;
explicit DrawSketchHandlerFillet(FilletType filletType)
: filletType(filletType)
, Mode(STATUS_SEEK_First)
, firstCurve(0)
namespace ConstructionMethods
{
enum class FilletConstructionMethod
{
Fillet,
Chamfer,
End // Must be the last one
};
}
using DSHFilletController =
DrawSketchDefaultWidgetController<DrawSketchHandlerFillet,
StateMachines::TwoSeekEnd,
/*PAutoConstraintSize =*/0,
/*OnViewParametersT =*/OnViewParameters<0, 0>, // NOLINT
/*WidgetParametersT =*/WidgetParameters<0, 0>, // NOLINT
/*WidgetCheckboxesT =*/WidgetCheckboxes<1, 1>, // NOLINT
/*WidgetComboboxesT =*/WidgetComboboxes<1, 1>, // NOLINT
ConstructionMethods::FilletConstructionMethod,
/*bool PFirstComboboxIsConstructionMethod =*/true>;
using DSHFilletControllerBase = DSHFilletController::ControllerBase;
using DrawSketchHandlerFilletBase = DrawSketchControllableHandler<DSHFilletController>;
class DrawSketchHandlerFillet: public DrawSketchHandlerFilletBase
{
friend DSHFilletController;
friend DSHFilletControllerBase;
public:
explicit DrawSketchHandlerFillet(ConstructionMethod constrMethod = ConstructionMethod::Fillet)
: DrawSketchHandlerFilletBase(constrMethod)
, preserveCorner(true)
, vtId(-1)
, geoId1(GeoEnum::GeoUndef)
, geoId2(GeoEnum::GeoUndef)
{}
~DrawSketchHandlerFillet() override
{
Gui::Selection().rmvSelectionGate();
}
enum SelectMode
private:
void updateDataAndDrawToPosition(Base::Vector2d onSketchPos) override
{
STATUS_SEEK_First,
STATUS_SEEK_Second
};
void mouseMove(Base::Vector2d onSketchPos) override
{
Q_UNUSED(onSketchPos);
switch (state()) {
case SelectMode::SeekFirst: {
vtId = getPreselectPoint();
geoId1 = getPreselectCurve();
firstPos = onSketchPos;
} break;
case SelectMode::SeekSecond: {
geoId2 = getPreselectCurve();
secondPos = onSketchPos;
} break;
default:
break;
}
}
bool pressButton(Base::Vector2d onSketchPos) override
void executeCommands() override
{
Q_UNUSED(onSketchPos);
return true;
}
SketchObject* obj = sketchgui->getSketchObject();
bool releaseButton(Base::Vector2d onSketchPos) override
{
bool construction = false;
int VtId = getPreselectPoint();
if (Mode == STATUS_SEEK_First && VtId != -1) {
bool isChamfer = constructionMethod() == ConstructionMethod::Chamfer;
if (vtId != -1) {
int GeoId;
Sketcher::PointPos PosId = Sketcher::PointPos::none;
sketchgui->getSketchObject()->getGeoVertexIndex(VtId, GeoId, PosId);
const Part::Geometry* geom = sketchgui->getSketchObject()->getGeometry(GeoId);
if (geom->is<Part::GeomLineSegment>()
&& (PosId == Sketcher::PointPos::start || PosId == Sketcher::PointPos::end)) {
PointPos PosId = PointPos::none;
obj->getGeoVertexIndex(vtId, GeoId, PosId);
const Part::Geometry* geom = obj->getGeometry(GeoId);
if (isLineSegment(*geom) && (PosId == PointPos::start || PosId == PointPos::end)) {
// guess fillet radius
double radius = -1;
std::vector<int> GeoIdList;
std::vector<Sketcher::PointPos> PosIdList;
sketchgui->getSketchObject()->getDirectlyCoincidentPoints(GeoId,
PosId,
GeoIdList,
PosIdList);
obj->getDirectlyCoincidentPoints(GeoId, PosId, GeoIdList, PosIdList);
if (GeoIdList.size() == 2 && GeoIdList[0] >= 0 && GeoIdList[1] >= 0) {
const Part::Geometry* geom1 =
sketchgui->getSketchObject()->getGeometry(GeoIdList[0]);
const Part::Geometry* geom2 =
sketchgui->getSketchObject()->getGeometry(GeoIdList[1]);
construction = Sketcher::GeometryFacade::getConstruction(geom1)
&& Sketcher::GeometryFacade::getConstruction(geom2);
if (geom1->is<Part::GeomLineSegment>() && geom2->is<Part::GeomLineSegment>()) {
const Part::GeomLineSegment* lineSeg1 =
static_cast<const Part::GeomLineSegment*>(geom1);
const Part::GeomLineSegment* lineSeg2 =
static_cast<const Part::GeomLineSegment*>(geom2);
Base::Vector3d dir1 = lineSeg1->getEndPoint() - lineSeg1->getStartPoint();
Base::Vector3d dir2 = lineSeg2->getEndPoint() - lineSeg2->getStartPoint();
if (PosIdList[0] == Sketcher::PointPos::end) {
const Part::Geometry* geo1 = obj->getGeometry(GeoIdList[0]);
const Part::Geometry* geo2 = obj->getGeometry(GeoIdList[1]);
construction = GeometryFacade::getConstruction(geo1)
&& GeometryFacade::getConstruction(geo2);
if (isLineSegment(*geo1) && isLineSegment(*geo2)) {
auto* line1 = static_cast<const Part::GeomLineSegment*>(geo1);
auto* line2 = static_cast<const Part::GeomLineSegment*>(geo2);
Base::Vector3d dir1 = line1->getEndPoint() - line1->getStartPoint();
Base::Vector3d dir2 = line2->getEndPoint() - line2->getStartPoint();
if (PosIdList[0] == PointPos::end) {
dir1 *= -1;
}
if (PosIdList[1] == Sketcher::PointPos::end) {
if (PosIdList[1] == PointPos::end) {
dir2 *= -1;
}
double l1 = dir1.Length();
@@ -169,55 +197,197 @@ public:
}
}
if (radius < 0) {
return false;
return;
}
int currentgeoid = getHighestCurveIndex();
int filletGeoId = getHighestCurveIndex() + (isChamfer ? 2 : 1);
// create fillet at point
try {
bool pointFillet = (filletType == 1);
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet"));
Gui::cmdAppObjectArgs(sketchgui->getObject(),
"fillet(%d,%d,%f,%s,%s)",
Gui::cmdAppObjectArgs(obj,
"fillet(%d,%d,%f,%s,%s,%s)",
GeoId,
static_cast<int>(PosId),
radius,
"True",
pointFillet ? "True" : "False");
preserveCorner ? "True" : "False",
isChamfer ? "True" : "False");
if (construction) {
Gui::cmdAppObjectArgs(sketchgui->getObject(),
"toggleConstruction(%d) ",
currentgeoid + 1);
Gui::cmdAppObjectArgs(obj, "toggleConstruction(%d) ", filletGeoId);
}
Gui::Command::commitCommand();
}
catch (const Base::Exception& e) {
Gui::NotifyUserError(
sketchgui->getObject(),
obj,
QT_TRANSLATE_NOOP("Notifications", "Failed to create fillet"),
e.what());
Gui::Command::abortCommand();
}
tryAutoRecomputeIfNotSolve(
static_cast<Sketcher::SketchObject*>(sketchgui->getObject()));
tryAutoRecomputeIfNotSolve(obj);
}
return true;
}
int GeoId = getPreselectCurve();
if (GeoId > -1) {
const Part::Geometry* geom = sketchgui->getSketchObject()->getGeometry(GeoId);
if (geom->isDerivedFrom<Part::GeomBoundedCurve>()) {
if (Mode == STATUS_SEEK_First) {
firstCurve = GeoId;
firstPos = onSketchPos;
Mode = STATUS_SEEK_Second;
// add the line to the selection
else {
Base::Vector3d refPnt1(firstPos.x, firstPos.y, 0.f);
Base::Vector3d refPnt2(secondPos.x, secondPos.y, 0.f);
const Part::Geometry* geo1 = obj->getGeometry(geoId1);
const Part::Geometry* geo2 = obj->getGeometry(geoId2);
construction =
GeometryFacade::getConstruction(geo1) && GeometryFacade::getConstruction(geo2);
double radius = 0;
if (isLineSegment(*geo1) && isLineSegment(*geo2)) {
// guess fillet radius
auto* line1 = static_cast<const Part::GeomLineSegment*>(geo1);
auto* line2 = static_cast<const Part::GeomLineSegment*>(geo2);
radius = Part::suggestFilletRadius(line1, line2, refPnt1, refPnt2);
if (radius < 0) {
return;
}
}
int filletGeoId = getHighestCurveIndex() + (isChamfer ? 2 : 1);
// create fillet between lines
try {
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet"));
Gui::cmdAppObjectArgs(
obj,
"fillet(%d,%d,App.Vector(%f,%f,0),App.Vector(%f,%f,0),%f,%s,%s,%s)",
geoId1,
geoId2,
firstPos.x,
firstPos.y,
secondPos.x,
secondPos.y,
radius,
"True",
preserveCorner ? "True" : "False",
isChamfer ? "True" : "False");
Gui::Command::commitCommand();
}
catch (const Base::CADKernelError& e) {
if (e.getTranslatable()) {
Gui::TranslatedUserError(sketchgui,
QObject::tr("CAD Kernel Error"),
QObject::tr(e.getMessage().c_str()));
}
Gui::Selection().clearSelection();
Gui::Command::abortCommand();
}
catch (const Base::ValueError& e) {
Gui::TranslatedUserError(sketchgui,
QObject::tr("Value Error"),
QObject::tr(e.getMessage().c_str()));
Gui::Selection().clearSelection();
Gui::Command::abortCommand();
}
tryAutoRecompute(obj);
if (construction) {
Gui::cmdAppObjectArgs(obj, "toggleConstruction(%d) ", filletGeoId);
}
Gui::Selection().clearSelection();
}
}
std::string getToolName() const override
{
return "DSH_Fillet";
}
QString getCrosshairCursorSVGName() const override
{
Gui::Selection().rmvSelectionGate();
Gui::Selection().addSelectionGate(new FilletSelection(sketchgui->getObject()));
if (constructionMethod() == DrawSketchHandlerFillet::ConstructionMethod::Fillet) {
if (preserveCorner) {
return QString::fromLatin1("Sketcher_Pointer_Create_PointFillet");
}
else {
return QString::fromLatin1("Sketcher_Pointer_Create_Fillet");
}
}
else {
if (preserveCorner) {
return QString::fromLatin1("Sketcher_Pointer_Create_PointChamfer");
}
else {
return QString::fromLatin1("Sketcher_Pointer_Create_Chamfer");
}
}
}
std::unique_ptr<QWidget> createWidget() const override
{
return std::make_unique<SketcherToolDefaultWidget>();
}
bool isWidgetVisible() const override
{
return true;
};
QPixmap getToolIcon() const override
{
return Gui::BitmapFactory().pixmap("Sketcher_CreateFillet");
}
QString getToolWidgetText() const override
{
return QString(QObject::tr("Fillet/Chamfer parameters"));
}
bool canGoToNextMode() override
{
if (state() == SelectMode::SeekFirst) {
if (vtId != -1) {
return true;
}
if (geoId1 >= 0) {
const Part::Geometry* geo = sketchgui->getSketchObject()->getGeometry(geoId1);
if (geo->isDerivedFrom<Part::GeomBoundedCurve>()) {
return true;
}
}
}
if (state() == SelectMode::SeekSecond) {
if (geoId2 >= 0) {
const Part::Geometry* geo = sketchgui->getSketchObject()->getGeometry(geoId2);
if (geo->isDerivedFrom<Part::GeomBoundedCurve>()) {
return true;
}
}
}
return false;
}
void onButtonPressed(Base::Vector2d onSketchPos) override
{
this->updateDataAndDrawToPosition(onSketchPos);
if (canGoToNextMode()) {
if (state() == SelectMode::SeekFirst) {
if (vtId != -1) {
setState(SelectMode::End);
}
else {
// add the edge to the selection
std::stringstream ss;
ss << "Edge" << firstCurve + 1;
ss << "Edge" << geoId1 + 1;
Gui::Selection().addSelection(
sketchgui->getSketchObject()->getDocument()->getName(),
sketchgui->getSketchObject()->getNameInDocument(),
@@ -225,128 +395,66 @@ public:
onSketchPos.x,
onSketchPos.y,
0.f);
}
else if (Mode == STATUS_SEEK_Second) {
int secondCurve = GeoId;
Base::Vector2d secondPos = onSketchPos;
Base::Vector3d refPnt1(firstPos.x, firstPos.y, 0.f);
Base::Vector3d refPnt2(secondPos.x, secondPos.y, 0.f);
const Part::Geometry* geom1 =
sketchgui->getSketchObject()->getGeometry(firstCurve);
double radius = 0;
if (geom->is<Part::GeomLineSegment>() && geom1->is<Part::GeomLineSegment>()) {
// guess fillet radius
const Part::GeomLineSegment* lineSeg1 =
static_cast<const Part::GeomLineSegment*>(
sketchgui->getSketchObject()->getGeometry(firstCurve));
const Part::GeomLineSegment* lineSeg2 =
static_cast<const Part::GeomLineSegment*>(
sketchgui->getSketchObject()->getGeometry(secondCurve));
radius = Part::suggestFilletRadius(lineSeg1, lineSeg2, refPnt1, refPnt2);
if (radius < 0) {
return false;
}
construction = Sketcher::GeometryFacade::getConstruction(lineSeg1)
&& Sketcher::GeometryFacade::getConstruction(lineSeg2);
}
else { // other supported curves
const Part::Geometry* geo1 = static_cast<const Part::Geometry*>(
sketchgui->getSketchObject()->getGeometry(firstCurve));
const Part::Geometry* geo2 = static_cast<const Part::Geometry*>(
sketchgui->getSketchObject()->getGeometry(secondCurve));
construction = Sketcher::GeometryFacade::getConstruction(geo1)
&& Sketcher::GeometryFacade::getConstruction(geo2);
}
int currentgeoid = getHighestCurveIndex();
// create fillet between lines
try {
bool pointFillet = (filletType == 1);
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet"));
Gui::cmdAppObjectArgs(
sketchgui->getObject(),
"fillet(%d,%d,App.Vector(%f,%f,0),App.Vector(%f,%f,0),%f,%s,%s)",
firstCurve,
secondCurve,
firstPos.x,
firstPos.y,
secondPos.x,
secondPos.y,
radius,
"True",
pointFillet ? "True" : "False");
Gui::Command::commitCommand();
}
catch (const Base::CADKernelError& e) {
if (e.getTranslatable()) {
Gui::TranslatedUserError(sketchgui,
QObject::tr("CAD Kernel Error"),
QObject::tr(e.getMessage().c_str()));
}
Gui::Selection().clearSelection();
Gui::Command::abortCommand();
Mode = STATUS_SEEK_First;
}
catch (const Base::ValueError& e) {
Gui::TranslatedUserError(sketchgui,
QObject::tr("Value Error"),
QObject::tr(e.getMessage().c_str()));
Gui::Selection().clearSelection();
Gui::Command::abortCommand();
Mode = STATUS_SEEK_First;
}
tryAutoRecompute(static_cast<Sketcher::SketchObject*>(sketchgui->getObject()));
if (construction) {
Gui::cmdAppObjectArgs(sketchgui->getObject(),
"toggleConstruction(%d) ",
currentgeoid + 1);
}
Gui::Selection().clearSelection();
Mode = STATUS_SEEK_First;
moveToNextMode();
}
}
else {
moveToNextMode();
}
}
if (VtId < 0 && GeoId < 0) { // exit the fillet tool if the user clicked on empty space
sketchgui
->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
}
return true;
}
private:
QString getCrosshairCursorSVGName() const override
{
Gui::Selection().rmvSelectionGate();
Gui::Selection().addSelectionGate(new FilletSelection(sketchgui->getObject()));
if (filletType == SimpleFillet) {
return QString::fromLatin1("Sketcher_Pointer_Create_Fillet");
}
else {
return QString::fromLatin1("Sketcher_Pointer_Create_PointFillet");
}
bool preserveCorner;
int vtId, geoId1, geoId2;
Base::Vector2d firstPos, secondPos;
};
template<>
void DSHFilletController::configureToolWidget()
{
if (!init) { // Code to be executed only upon initialisation
QStringList names = {QStringLiteral("Fillet"), QStringLiteral("Chamfer")};
toolWidget->setComboboxElements(WCombobox::FirstCombo, names);
toolWidget->setComboboxItemIcon(
WCombobox::FirstCombo,
0,
Gui::BitmapFactory().iconFromTheme("Sketcher_CreateFillet"));
toolWidget->setComboboxItemIcon(
WCombobox::FirstCombo,
1,
Gui::BitmapFactory().iconFromTheme("Sketcher_CreateChamfer"));
toolWidget->setCheckboxLabel(
WCheckbox::FirstBox,
QApplication::translate("TaskSketcherTool_c1_fillet", "Preserve corner (U)"));
toolWidget->setCheckboxToolTip(
WCheckbox::FirstBox,
QApplication::translate("TaskSketcherTool_c1_fillet",
"Preserves intersection point and most constraints"));
toolWidget->setCheckboxIcon(
WCheckbox::FirstBox,
Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePointFillet"));
}
syncCheckboxToHandler(WCheckbox::FirstBox, handler->preserveCorner);
}
template<>
void DSHFilletController::adaptDrawingToCheckboxChange(int checkboxindex, bool value)
{
Q_UNUSED(checkboxindex);
switch (checkboxindex) {
case WCheckbox::FirstBox:
handler->preserveCorner = value;
break;
}
protected:
int filletType;
SelectMode Mode;
int firstCurve;
Base::Vector2d firstPos;
};
handler->updateCursor();
}
} // namespace SketcherGui

View File

@@ -139,6 +139,7 @@
<file>icons/geometry/Sketcher_CreateBSplineByInterpolation_Constr.svg</file>
<file>icons/geometry/Sketcher_Create_Periodic_BSplineByInterpolation.svg</file>
<file>icons/geometry/Sketcher_Create_Periodic_BSplineByInterpolation_Constr.svg</file>
<file>icons/geometry/Sketcher_CreateChamfer.svg</file>
<file>icons/geometry/Sketcher_CreateCircle.svg</file>
<file>icons/geometry/Sketcher_CreateCircle_Constr.svg</file>
<file>icons/geometry/Sketcher_CreateEllipse_3points.svg</file>
@@ -229,6 +230,7 @@
<file>icons/pointers/Sketcher_Pointer_Create_BSpline.svg</file>
<file>icons/pointers/Sketcher_Pointer_Create_BSplineByInterpolation.svg</file>
<file>icons/pointers/Sketcher_Pointer_Create_Circle.svg</file>
<file>icons/pointers/Sketcher_Pointer_Create_Chamfer.svg</file>
<file>icons/pointers/Sketcher_Pointer_Create_EllipseByCenter.svg</file>
<file>icons/pointers/Sketcher_Pointer_Create_Ellipse_3points.svg</file>
<file>icons/pointers/Sketcher_Pointer_Create_Fillet.svg</file>
@@ -238,6 +240,7 @@
<file>icons/pointers/Sketcher_Pointer_Create_Periodic_BSpline.svg</file>
<file>icons/pointers/Sketcher_Pointer_Create_Periodic_BSplineByInterpolation.svg</file>
<file>icons/pointers/Sketcher_Pointer_Create_Point.svg</file>
<file>icons/pointers/Sketcher_Pointer_Create_PointChamfer.svg</file>
<file>icons/pointers/Sketcher_Pointer_Create_PointFillet.svg</file>
<file>icons/pointers/Sketcher_Pointer_Create_Translate.svg</file>
<file>icons/pointers/Sketcher_Pointer_Create_Offset.svg</file>

View File

@@ -0,0 +1,537 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="64"
height="64"
id="svg2869"
version="1.1"
viewBox="0 0 64 64"
sodipodi:docname="Sketcher_CreateChamfer.svg"
inkscape:version="1.1-beta1 (77e7b44db3, 2021-03-28)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
id="namedview89"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
objecttolerance="10.0"
gridtolerance="10.0"
guidetolerance="10.0"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="23.53125"
inkscape:cx="29.173971"
inkscape:cy="38.438247"
inkscape:window-width="3840"
inkscape:window-height="1571"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="svg2869" />
<defs
id="defs2871">
<linearGradient
id="linearGradient34">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop33" />
<stop
style="stop-color:#d3d7cf;stop-opacity:1;"
offset="1"
id="stop34" />
</linearGradient>
<linearGradient
id="linearGradient32">
<stop
style="stop-color:#d3d7cf;stop-opacity:1;"
offset="0"
id="stop31" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop32" />
</linearGradient>
<linearGradient
id="linearGradient17">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop16" />
<stop
style="stop-color:#ffffff;stop-opacity:0"
offset="1"
id="stop17" />
</linearGradient>
<linearGradient
id="linearGradient5">
<stop
style="stop-color:#ef2929;stop-opacity:1;"
offset="0"
id="stop19" />
<stop
style="stop-color:#ef2929;stop-opacity:0;"
offset="1"
id="stop20" />
</linearGradient>
<linearGradient
id="swatch18">
<stop
style="stop-color:#ef2929;stop-opacity:1;"
offset="0"
id="stop18" />
</linearGradient>
<linearGradient
id="swatch15">
<stop
style="stop-color:#3d0000;stop-opacity:1;"
offset="0"
id="stop15" />
</linearGradient>
<linearGradient
id="linearGradient5-1">
<stop
style="stop-color:#ef2929;stop-opacity:1;"
offset="0"
id="stop5" />
<stop
style="stop-color:#ef2929;stop-opacity:0;"
offset="1"
id="stop6" />
</linearGradient>
<linearGradient
id="linearGradient3836-9">
<stop
style="stop-color:#a40000;stop-opacity:1"
offset="0"
id="stop3838-8" />
<stop
style="stop-color:#ef2929;stop-opacity:1"
offset="1"
id="stop3840-1" />
</linearGradient>
<linearGradient
id="linearGradient3836-9-3">
<stop
style="stop-color:#a40000;stop-opacity:1"
offset="0"
id="stop3838-8-5" />
<stop
style="stop-color:#ef2929;stop-opacity:1"
offset="1"
id="stop3840-1-6" />
</linearGradient>
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientUnits="userSpaceOnUse"
id="linearGradient3082"
xlink:href="#linearGradient3836-9-3" />
<linearGradient
id="linearGradient3836-9-7">
<stop
style="stop-color:#a40000;stop-opacity:1"
offset="0"
id="stop3838-8-0" />
<stop
style="stop-color:#ef2929;stop-opacity:1"
offset="1"
id="stop3840-1-9" />
</linearGradient>
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientUnits="userSpaceOnUse"
id="linearGradient3082-3"
xlink:href="#linearGradient3836-9-7" />
<linearGradient
xlink:href="#linearGradient3836-9-3"
id="linearGradient3801-1-3"
gradientUnits="userSpaceOnUse"
x1="-18"
y1="18"
x2="-22"
y2="5"
gradientTransform="matrix(0.76342439,0,0,0.75750425,-4.596389,2.7525637)" />
<linearGradient
xlink:href="#linearGradient3836-9-3"
id="linearGradient3801-1-3-2"
gradientUnits="userSpaceOnUse"
x1="-18"
y1="18"
x2="-22"
y2="5"
gradientTransform="matrix(0.84956703,0,0,0.84301394,-2.927337,1.7790378)" />
<radialGradient
xlink:href="#linearGradient3809"
id="radialGradient3815"
cx="225.93762"
cy="91.956673"
fx="225.93762"
fy="91.956673"
r="22"
gradientTransform="matrix(-1.4090915,3.8636359,-0.97565325,-0.35582669,437.08461,-816.22007)"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3809">
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="0"
id="stop3811" />
<stop
style="stop-color:#204a87;stop-opacity:1"
offset="1"
id="stop3813" />
</linearGradient>
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientUnits="userSpaceOnUse"
id="linearGradient3444"
xlink:href="#linearGradient3836-9-3"
gradientTransform="matrix(0.90206746,0,0,0.90216902,-1.9060863,1.1084289)" />
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientUnits="userSpaceOnUse"
id="linearGradient3857"
xlink:href="#linearGradient3836-9-3"
gradientTransform="matrix(0.89262616,0,0,0.89258466,72.894067,1.2176306)" />
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientTransform="matrix(0.93724177,0,0,0.93725692,-1.2227671,0.70650014)"
gradientUnits="userSpaceOnUse"
id="linearGradient3148"
xlink:href="#linearGradient3836-9-3" />
<linearGradient
xlink:href="#linearGradient17"
id="linearGradient3898"
x1="37.429146"
y1="41.590584"
x2="24.483221"
y2="4.9104676"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3892">
<stop
style="stop-color:#bdd2e9;stop-opacity:1;"
offset="0"
id="stop3894" />
<stop
style="stop-color:#ffffff;stop-opacity:0"
offset="1"
id="stop3896" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient3892"
id="linearGradient3856"
x1="22.84341"
y1="4.8241611"
x2="30.783579"
y2="28.644661"
gradientUnits="userSpaceOnUse" />
<radialGradient
r="22"
fy="91.956673"
fx="225.93762"
cy="91.956673"
cx="225.93762"
gradientTransform="matrix(-1.7064667,4.6731721,-1.1815555,-0.43038201,776.9032,-933.08315)"
gradientUnits="userSpaceOnUse"
id="radialGradient3163"
xlink:href="#linearGradient3809" />
<linearGradient
xlink:href="#linearGradient4104"
id="linearGradient4110"
x1="-23.070524"
y1="18.383886"
x2="-24.194258"
y2="6.534451"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient4104">
<stop
style="stop-color:#d3d7cf;stop-opacity:1"
offset="0"
id="stop4106" />
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="1"
id="stop4108" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient4096"
id="linearGradient4102"
x1="-24.035076"
y1="16.85112"
x2="-23.821426"
y2="7.2881389"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient4096">
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="0"
id="stop4098" />
<stop
style="stop-color:#d3d7cf;stop-opacity:1"
offset="1"
id="stop4100" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient3836-9-3"
id="linearGradient4216"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.71430595,0,0,0.71426219,259.5032,71.709694)"
x1="-18"
y1="18"
x2="-22"
y2="5" />
<radialGradient
r="22"
fy="91.956673"
fx="225.93762"
cy="91.956673"
cx="225.93762"
gradientTransform="matrix(-1.7064667,4.6731721,-1.1815555,-0.43038201,776.9032,-933.08315)"
gradientUnits="userSpaceOnUse"
id="radialGradient3163-9"
xlink:href="#linearGradient3809" />
<linearGradient
xlink:href="#linearGradient4104"
id="linearGradient4347"
x1="-21.31983"
y1="18.008659"
x2="-24.907471"
y2="6.9377007"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient4096"
id="linearGradient4339"
x1="-21.141161"
y1="17.489555"
x2="-24.733845"
y2="7.0083036"
gradientUnits="userSpaceOnUse" />
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientTransform="matrix(0.65313239,0,0,0.65304994,258.31758,72.40809)"
gradientUnits="userSpaceOnUse"
id="linearGradient4235-2"
xlink:href="#linearGradient3836-9-3" />
<linearGradient
xlink:href="#linearGradient3836-9-3"
id="linearGradient3801-1-3-7"
gradientUnits="userSpaceOnUse"
x1="-18"
y1="18"
x2="-22"
y2="5" />
<linearGradient
xlink:href="#linearGradient3836-9-3"
id="linearGradient29"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.82607043,0,0,0.82533448,-4.0098079,1.346708)"
x1="-18"
y1="18"
x2="-22"
y2="5" />
<linearGradient
xlink:href="#linearGradient32"
id="linearGradient3901"
gradientUnits="userSpaceOnUse"
x1="30.202745"
y1="56.729507"
x2="35.013981"
y2="19.843365"
gradientTransform="matrix(0.62354045,-0.62354046,0.62354039,0.62354042,-12.205772,28.459494)" />
<linearGradient
xlink:href="#linearGradient34"
id="linearGradient3903"
gradientUnits="userSpaceOnUse"
x1="29.993565"
y1="54.846851"
x2="36.094769"
y2="20.854424"
gradientTransform="matrix(0.57365718,-0.57365723,0.57365712,0.57365719,-8.6693108,28.742734)" />
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientUnits="userSpaceOnUse"
id="linearGradient3922"
xlink:href="#linearGradient3836-9-3"
gradientTransform="matrix(0.85221122,0,0,0.85228409,-2.8500258,1.6945624)" />
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientUnits="userSpaceOnUse"
id="linearGradient3096"
xlink:href="#linearGradient3836-9-3" />
<linearGradient
xlink:href="#linearGradient3836-9-3"
id="linearGradient38"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.82607043,0,0,0.82533448,-4.0098079,1.346708)"
x1="-18"
y1="18"
x2="-22"
y2="5" />
<linearGradient
xlink:href="#linearGradient3836-9-3"
id="linearGradient3801-7"
gradientUnits="userSpaceOnUse"
x1="-18"
y1="18"
x2="-22"
y2="5"
gradientTransform="translate(-90,-5.9999999)" />
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientUnits="userSpaceOnUse"
id="linearGradient3082-3-8"
xlink:href="#linearGradient3836-9-3" />
</defs>
<metadata
id="metadata2874">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:creator>
<cc:Agent>
<dc:title>[maxwxyz]</dc:title>
</cc:Agent>
</dc:creator>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreateArc.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<dc:date>2023-12-19</dc:date>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="g3401"
transform="matrix(0.146567,0,0,0.146567,70.402356,-55.12963)">
<path
style="fill:#d3d7cf;stroke:#151819;stroke-width:13.6456;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -64.150569,751.39444 v -40.93691 l -68.227781,0.13987 c -245.62106,-231.83597 0,0 -245.62106,-231.83597 -0.11268,-0.10636 -8.1e-4,-82.15354 -8.1e-4,-82.15354 h -40.93691 v 98.11086 c 271.77108,256.96565 2.11823,0.15342 271.77108,256.96565 v 0 z"
id="path3077"
sodipodi:nodetypes="cccscccccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:13.6456;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -139.20159,724.1032 c -252.44428,-238.79865 0,0 -252.44428,-238.79865"
id="path3892"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:13.6456;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -405.2915,403.4307 v 81.87383"
id="path3079" />
<path
style="fill:none;stroke:#ffffff;stroke-width:13.6456;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M -70.973388,724.10317 H -146.02439"
id="path3081" />
<rect
style="fill:#d3d7cf;fill-opacity:1;stroke:#151819;stroke-width:13.6456;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:1.9;stroke-dasharray:none;stroke-opacity:1"
id="rect3083"
width="27.291277"
height="54.582546"
x="-418.93713"
y="737.74866" />
<rect
style="fill:#d3d7cf;fill-opacity:1;stroke:#151819;stroke-width:13.6456;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:1.9;stroke-dasharray:none;stroke-opacity:1"
id="rect3083-3"
width="27.291368"
height="54.582546"
x="-418.93723"
y="642.22906" />
<rect
style="fill:#d3d7cf;fill-opacity:1;stroke:#151819;stroke-width:13.6456;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:1.9;stroke-dasharray:none;stroke-opacity:1"
id="rect3083-7"
width="27.291306"
height="68.228004"
x="724.10321"
y="391.64584"
transform="rotate(90)" />
<rect
style="fill:#d3d7cf;fill-opacity:1;stroke:none;stroke-width:6.44377;stroke-miterlimit:4;stroke-dasharray:none"
id="rect3083-5"
width="13.645644"
height="54.582569"
x="-412.11432"
y="730.92596" />
<rect
style="fill:#d3d7cf;fill-opacity:1;stroke:#151819;stroke-width:13.6456;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:1.9;stroke-dasharray:none;stroke-opacity:1"
id="rect3083-35"
width="27.291306"
height="54.582546"
x="724.10321"
y="309.77203"
transform="rotate(90)" />
</g>
<g
transform="matrix(0.77869459,0,0,0.77869445,63.667786,43.342778)"
id="g29"
style="stroke-width:1.2842">
<path
style="fill:none;stroke:#2e0000;stroke-width:2.56841;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28"
d="M -26.310778,5.3580033 A 8.3519646,8.3515832 0.02039876 1 1 -13.623399,16.222662 8.3519646,8.3515832 0.02039876 1 1 -26.310778,5.3580033 Z" />
<path
style="fill:url(#linearGradient29);fill-opacity:1;stroke:#ef2929;stroke-width:2.56842;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path29"
d="m -24.358888,7.0362241 a 5.782493,5.7773415 0 1 1 8.784093,7.5158349 5.782493,5.7773415 0 0 1 -8.784093,-7.5158349 z" />
</g>
<g
transform="matrix(0.77869459,0,0,0.77869445,27.71825,8.6680131)"
id="g38"
style="stroke-width:1.2842">
<path
style="fill:none;stroke:#2e0000;stroke-width:2.56841;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path37"
d="M -26.310778,5.3580033 A 8.3519646,8.3515832 0.02039876 1 1 -13.623399,16.222662 8.3519646,8.3515832 0.02039876 1 1 -26.310778,5.3580033 Z" />
<path
style="fill:url(#linearGradient38);fill-opacity:1;stroke:#ef2929;stroke-width:2.56842;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path38"
d="m -24.358888,7.0362241 a 5.782493,5.7773415 0 1 1 8.784093,7.5158349 5.782493,5.7773415 0 0 1 -8.784093,-7.5158349 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
height="64"
width="64"
id="svg10"
sodipodi:docname="Sketcher_Pointer_Create_Chamfer.svg"
inkscape:version="1.1-beta1 (77e7b44db3, 2021-03-28)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="30.640625"
inkscape:cy="51.484375"
inkscape:window-width="3840"
inkscape:window-height="1571"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="symbol"
objecttolerance="10.0"
gridtolerance="10.0"
guidetolerance="10.0"
inkscape:pageshadow="0"
showgrid="false" />
<defs
id="defs14">
<inkscape:path-effect
effect="dashed_stroke"
id="path-effect4"
is_visible="true"
lpeversion="1"
numberdashes="3"
holefactor="-0.16"
splitsegments="true"
halfextreme="false"
unifysegment="false"
message="Add &lt;b&gt;&quot;Fill Between Many LPE&quot;&lt;/b&gt; to add fill." />
<inkscape:path-effect
effect="dashed_stroke"
id="path-effect3"
is_visible="true"
lpeversion="1"
numberdashes="3"
holefactor="0.18"
splitsegments="true"
halfextreme="false"
unifysegment="false"
message="Add &lt;b&gt;&quot;Fill Between Many LPE&quot;&lt;/b&gt; to add fill." />
<inkscape:path-effect
effect="dashed_stroke"
id="path-effect2"
is_visible="true"
lpeversion="1"
numberdashes="3"
holefactor="0"
splitsegments="true"
halfextreme="false"
unifysegment="true"
message="Add &lt;b&gt;&quot;Fill Between Many LPE&quot;&lt;/b&gt; to add fill." />
<inkscape:path-effect
effect="dashed_stroke"
id="path-effect1"
is_visible="true"
lpeversion="1"
numberdashes="4"
holefactor="0.11"
splitsegments="true"
halfextreme="false"
unifysegment="true"
message="Add &lt;b&gt;&quot;Fill Between Many LPE&quot;&lt;/b&gt; to add fill." />
<linearGradient
xlink:href="#linearGradient3836-9"
id="linearGradient3801-7"
gradientUnits="userSpaceOnUse"
x1="-18"
y1="18"
x2="-22"
y2="5"
gradientTransform="translate(-90,-5.9999999)" />
<linearGradient
id="linearGradient3836-9">
<stop
style="stop-color:#a40000;stop-opacity:1"
offset="0"
id="stop3838-8" />
<stop
style="stop-color:#ef2929;stop-opacity:1"
offset="1"
id="stop3840-1" />
</linearGradient>
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientUnits="userSpaceOnUse"
id="linearGradient3082-3"
xlink:href="#linearGradient3836-9" />
</defs>
<g
id="symbol"
style="display:inline;fill:none;stroke:#cc0000;stroke-width:2"
transform="translate(4,-2)">
<circle
cx="26.439085"
cy="38.106495"
r="4"
id="circle4" />
<circle
cx="42.978943"
cy="54.646351"
r="4"
id="circle4-9"
style="display:inline;fill:none;stroke:#cc0000;stroke-width:2" />
<path
id="rect4"
d="M 53.021065,54.646352 H 42.978943 c -16.539858,-16.539858 0,0 -16.539858,-16.539858 V 28.064372"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#cc0000;stroke-width:2;stroke-linecap:butt;stroke-dasharray:none;stroke-dashoffset:0"
d="m 26.439086,53.646352 v 5"
id="path4"
sodipodi:nodetypes="cc" />
<path
style="display:inline;fill:none;stroke:#cc0000;stroke-width:2;stroke-linecap:butt;stroke-dasharray:none;stroke-dashoffset:0"
d="m 26.439086,47.646352 v 3"
id="path4-9"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#cc0000;stroke-width:2;stroke-linecap:butt;stroke-dasharray:none;stroke-dashoffset:0"
d="m 27.439086,54.646352 -5.000001,-10e-7"
id="path5"
sodipodi:nodetypes="cc" />
<path
style="display:inline;fill:none;stroke:#cc0000;stroke-width:2;stroke-linecap:butt;stroke-dasharray:none;stroke-dashoffset:0"
d="m 33.439086,54.646352 -3.000001,-10e-7"
id="path5-5"
sodipodi:nodetypes="cc" />
</g>
<g
id="crosshair"
style="stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter">
<path
d="m16,3v9m0,8v9m-13-13h9m8,0h9"
id="path7" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
height="64"
width="64"
id="svg10"
sodipodi:docname="Sketcher_Pointer_Create_PointChamfer.svg"
inkscape:version="1.1-beta1 (77e7b44db3, 2021-03-28)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="11.313708"
inkscape:cx="6.7617086"
inkscape:cy="43.000931"
inkscape:window-width="3840"
inkscape:window-height="1571"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="symbol"
objecttolerance="10.0"
gridtolerance="10.0"
guidetolerance="10.0"
inkscape:pageshadow="0"
showgrid="false" />
<defs
id="defs14">
<inkscape:path-effect
effect="dashed_stroke"
id="path-effect4"
is_visible="true"
lpeversion="1"
numberdashes="3"
holefactor="-0.16"
splitsegments="true"
halfextreme="false"
unifysegment="false"
message="Add &lt;b&gt;&quot;Fill Between Many LPE&quot;&lt;/b&gt; to add fill." />
<inkscape:path-effect
effect="dashed_stroke"
id="path-effect3"
is_visible="true"
lpeversion="1"
numberdashes="3"
holefactor="0.18"
splitsegments="true"
halfextreme="false"
unifysegment="false"
message="Add &lt;b&gt;&quot;Fill Between Many LPE&quot;&lt;/b&gt; to add fill." />
<inkscape:path-effect
effect="dashed_stroke"
id="path-effect2"
is_visible="true"
lpeversion="1"
numberdashes="3"
holefactor="0"
splitsegments="true"
halfextreme="false"
unifysegment="true"
message="Add &lt;b&gt;&quot;Fill Between Many LPE&quot;&lt;/b&gt; to add fill." />
<inkscape:path-effect
effect="dashed_stroke"
id="path-effect1"
is_visible="true"
lpeversion="1"
numberdashes="4"
holefactor="0.11"
splitsegments="true"
halfextreme="false"
unifysegment="true"
message="Add &lt;b&gt;&quot;Fill Between Many LPE&quot;&lt;/b&gt; to add fill." />
<linearGradient
xlink:href="#linearGradient3836-9"
id="linearGradient3801-7"
gradientUnits="userSpaceOnUse"
x1="-18"
y1="18"
x2="-22"
y2="5"
gradientTransform="translate(-90,-5.9999999)" />
<linearGradient
id="linearGradient3836-9">
<stop
style="stop-color:#a40000;stop-opacity:1"
offset="0"
id="stop3838-8" />
<stop
style="stop-color:#ef2929;stop-opacity:1"
offset="1"
id="stop3840-1" />
</linearGradient>
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientUnits="userSpaceOnUse"
id="linearGradient3082-3"
xlink:href="#linearGradient3836-9" />
</defs>
<g
id="symbol"
style="display:inline;fill:none;stroke:#cc0000;stroke-width:2"
transform="translate(4,-2)">
<circle
cx="26.439085"
cy="38.106495"
r="4"
id="circle4" />
<circle
cx="42.978943"
cy="54.646351"
r="4"
id="circle4-9"
style="display:inline;fill:none;stroke:#cc0000;stroke-width:2" />
<circle
cx="26.439085"
cy="54.646351"
r="4"
id="circle4-9-7"
style="display:inline;fill:none;stroke:#cc0000;stroke-width:2" />
<path
id="rect4"
d="M 53.021065,54.646352 H 42.978943 c -16.539858,-16.539858 0,0 -16.539858,-16.539858 V 28.064372"
sodipodi:nodetypes="cccc" />
</g>
<g
id="crosshair"
style="stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter">
<path
d="m16,3v9m0,8v9m-13-13h9m8,0h9"
id="path7" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -4161,7 +4161,8 @@ void ViewProviderSketch::generateContextMenu()
<< "Separator"
<< "Sketcher_ToggleConstruction"
<< "Separator"
<< "Sketcher_CreatePointFillet"
<< "Sketcher_CreateFillet"
<< "Sketcher_CreateChamfer"
<< "Sketcher_Trimming"
<< "Sketcher_Extend"
<< "Separator"

View File

@@ -375,8 +375,8 @@ void SketcherAddWorkspaceFillets(T& geom);
template<>
inline void SketcherAddWorkspaceFillets<Gui::MenuItem>(Gui::MenuItem& geom)
{
geom << "Sketcher_CreatePointFillet"
<< "Sketcher_CreateFillet";
geom << "Sketcher_CreateFillet"
<< "Sketcher_CreateChamfer";
}
template<>