diff --git a/src/Mod/Part/App/Geometry.cpp b/src/Mod/Part/App/Geometry.cpp
index fa2ed4043e..fe7ac8e971 100644
--- a/src/Mod/Part/App/Geometry.cpp
+++ b/src/Mod/Part/App/Geometry.cpp
@@ -2957,6 +2957,18 @@ Base::Vector3d GeomArcOfEllipse::getMajorAxisDir() const
return Base::Vector3d(xdir.X(), xdir.Y(), xdir.Z());
}
+/*!
+ * \brief GeomArcOfEllipse::getMinorAxisDir
+ * \return the direction vector (unit-length) of minor axis of the ellipse.
+ */
+Base::Vector3d GeomArcOfEllipse::getMinorAxisDir() const
+{
+ Handle(Geom_Ellipse) c = Handle(Geom_Ellipse)::DownCast(myCurve->BasisCurve());
+ assert(!c.IsNull());
+ gp_Dir ydir = c->YAxis().Direction();
+ return Base::Vector3d(ydir.X(), ydir.Y(), ydir.Z());
+}
+
/*!
* \brief GeomArcOfEllipse::setMajorAxisDir Rotates the ellipse in its plane, so
* that its major axis is as close as possible to the provided direction.
@@ -3387,6 +3399,18 @@ Base::Vector3d GeomArcOfHyperbola::getMajorAxisDir() const
return Base::Vector3d(xdir.X(), xdir.Y(), xdir.Z());
}
+/*!
+ * \brief GeomArcOfHyperbola::getMinorAxisDir
+ * \return the direction vector (unit-length) of minor axis of the hyperbola.
+ */
+Base::Vector3d GeomArcOfHyperbola::getMinorAxisDir() const
+{
+ Handle(Geom_Hyperbola) c = Handle(Geom_Hyperbola)::DownCast( myCurve->BasisCurve() );
+ assert(!c.IsNull());
+ gp_Dir ydir = c->YAxis().Direction();
+ return Base::Vector3d(ydir.X(), ydir.Y(), ydir.Z());
+}
+
/*!
* \brief GeomArcOfHyperbola::setMajorAxisDir Rotates the hyperbola in its plane, so
* that its major axis is as close as possible to the provided direction.
diff --git a/src/Mod/Part/App/Geometry.h b/src/Mod/Part/App/Geometry.h
index aa92f85c2e..c2027c9e27 100644
--- a/src/Mod/Part/App/Geometry.h
+++ b/src/Mod/Part/App/Geometry.h
@@ -568,6 +568,7 @@ public:
void setMinorRadius(double Radius);
Base::Vector3d getMajorAxisDir() const;
void setMajorAxisDir(Base::Vector3d newdir);
+ Base::Vector3d getMinorAxisDir() const;
void getRange(double& u, double& v, bool emulateCCWXY) const override;
void setRange(double u, double v, bool emulateCCWXY) override;
@@ -630,6 +631,7 @@ public:
void setMinorRadius(double Radius);
Base::Vector3d getMajorAxisDir() const;
void setMajorAxisDir(Base::Vector3d newdir);
+ Base::Vector3d getMinorAxisDir() const;
void getRange(double& u, double& v, bool emulateCCWXY) const override;
void setRange(double u, double v, bool emulateCCWXY) override;
diff --git a/src/Mod/Sketcher/App/ConstraintPyImp.cpp b/src/Mod/Sketcher/App/ConstraintPyImp.cpp
index df684a6914..8a15fdb4fb 100644
--- a/src/Mod/Sketcher/App/ConstraintPyImp.cpp
+++ b/src/Mod/Sketcher/App/ConstraintPyImp.cpp
@@ -619,6 +619,12 @@ std::string ConstraintPy::representation() const
case ParabolaFocus:
result << "'InternalAlignment:ParabolaFocus'>";
break;
+ case BSplineControlPoint:
+ result << "'InternalAlignment:BSplineControlPoint'>";
+ break;
+ case BSplineKnotPoint:
+ result << "'InternalAlignment:BSplineKnotPoint'>";
+ break;
default:
result << "'InternalAlignment:?'>";
break;
diff --git a/src/Mod/Sketcher/App/PythonConverter.cpp b/src/Mod/Sketcher/App/PythonConverter.cpp
index 184b770d7b..d063378161 100644
--- a/src/Mod/Sketcher/App/PythonConverter.cpp
+++ b/src/Mod/Sketcher/App/PythonConverter.cpp
@@ -29,13 +29,14 @@
#include
#include
#include
+#include
#include "PythonConverter.h"
using namespace Sketcher;
-std::string PythonConverter::convert(const Part::Geometry* geo)
+std::string PythonConverter::convert(const Part::Geometry* geo, Mode mode)
{
// "addGeometry(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%s)"
@@ -45,6 +46,16 @@ std::string PythonConverter::convert(const Part::Geometry* geo)
command = boost::str(boost::format("addGeometry(%s,%s)\n") % sg.creation
% (sg.construction ? "True" : "False"));
+ if ((geo->getTypeId() != Part::GeomEllipse::getClassTypeId()
+ || geo->getTypeId() != Part::GeomArcOfEllipse::getClassTypeId()
+ || geo->getTypeId() != Part::GeomArcOfHyperbola::getClassTypeId()
+ || geo->getTypeId() != Part::GeomArcOfParabola::getClassTypeId()
+ || geo->getTypeId() != Part::GeomBSplineCurve::getClassTypeId())
+ && mode == Mode::CreateInternalGeometry) {
+ command +=
+ boost::str(boost::format("exposeInternalGeometry(len(ActiveSketch.Geometry))\n"));
+ }
+
return command;
}
@@ -60,7 +71,8 @@ std::string PythonConverter::convert(const Sketcher::Constraint* constraint)
}
std::string PythonConverter::convert(const std::string& doc,
- const std::vector& geos)
+ const std::vector& geos,
+ Mode mode)
{
if (geos.empty()) {
return std::string();
@@ -87,7 +99,7 @@ std::string PythonConverter::convert(const std::string& doc,
return command;
};
- std::string command;
+ std::string command = boost::str(boost::format("lastGeoId = len(ActiveSketch.Geometry)\n"));
// Adds a list of consecutive geometries of a same construction type to the generating command
auto addToCommands = [&command,
@@ -133,6 +145,22 @@ std::string PythonConverter::convert(const std::string& doc,
addToCommands(geolist, ngeos, currentconstruction);
+ int index = 0;
+ if (mode == Mode::CreateInternalGeometry) {
+ for (auto geo : geos) {
+ index++;
+ if (geo->getTypeId() != Part::GeomEllipse::getClassTypeId()
+ || geo->getTypeId() != Part::GeomArcOfEllipse::getClassTypeId()
+ || geo->getTypeId() != Part::GeomArcOfHyperbola::getClassTypeId()
+ || geo->getTypeId() != Part::GeomArcOfParabola::getClassTypeId()
+ || geo->getTypeId() != Part::GeomBSplineCurve::getClassTypeId()) {
+ std::string newcommand =
+ boost::str(boost::format("exposeInternalGeometry(lastGeoId + %d)\n") % (index));
+ command += newcommand;
+ }
+ }
+ }
+
return command;
}
@@ -206,11 +234,9 @@ PythonConverter::SingleGeometry PythonConverter::process(const Part::Geometry* g
[](const Part::Geometry* geo) {
auto ellipse = static_cast(geo);
SingleGeometry sg;
- auto periapsis =
- ellipse->getCenter() + ellipse->getMajorAxisDir() * ellipse->getMajorRadius();
- auto positiveB =
- ellipse->getCenter() + ellipse->getMinorAxisDir() * ellipse->getMinorRadius();
auto center = ellipse->getCenter();
+ auto periapsis = center + ellipse->getMajorAxisDir() * ellipse->getMajorRadius();
+ auto positiveB = center + ellipse->getMinorAxisDir() * ellipse->getMinorRadius();
sg.creation =
boost::str(boost::format("Part.Ellipse(App.Vector(%f, %f, %f), App.Vector(%f, "
"%f, %f), App.Vector(%f, %f, %f))")
@@ -219,6 +245,77 @@ PythonConverter::SingleGeometry PythonConverter::process(const Part::Geometry* g
sg.construction = Sketcher::GeometryFacade::getConstruction(geo);
return sg;
}},
+ {Part::GeomArcOfEllipse::getClassTypeId(),
+ [](const Part::Geometry* geo) {
+ auto aoe = static_cast(geo);
+ SingleGeometry sg;
+ auto center = aoe->getCenter();
+ auto periapsis = center + aoe->getMajorAxisDir() * aoe->getMajorRadius();
+ auto positiveB = center + aoe->getMinorAxisDir() * aoe->getMinorRadius();
+ sg.creation = boost::str(
+ boost::format(
+ "Part.ArcOfEllipse(Part.Ellipse(App.Vector(%f, %f, %f), App.Vector(%f, "
+ "%f, %f), App.Vector(%f, %f, %f)), %f, %f)")
+ % periapsis.x % periapsis.y % periapsis.z % positiveB.x % positiveB.y
+ % positiveB.z % center.x % center.y % center.z % aoe->getFirstParameter()
+ % aoe->getLastParameter());
+ sg.construction = Sketcher::GeometryFacade::getConstruction(geo);
+ return sg;
+ }},
+ {Part::GeomArcOfHyperbola::getClassTypeId(),
+ [](const Part::Geometry* geo) {
+ auto aoh = static_cast(geo);
+ SingleGeometry sg;
+ auto center = aoh->getCenter();
+ auto majAxisPoint = center + aoh->getMajorAxisDir() * aoh->getMajorRadius();
+ auto minAxisPoint = center + aoh->getMinorAxisDir() * aoh->getMinorRadius();
+ sg.creation = boost::str(
+ boost::format("Part.ArcOfHyperbola(Part.Hyperbola(App.Vector(%f, %f, %f), "
+ "App.Vector(%f, %f, %f), App.Vector(%f, %f, %f)), %f, %f)")
+ % majAxisPoint.x % majAxisPoint.y % majAxisPoint.z % minAxisPoint.x
+ % minAxisPoint.y % minAxisPoint.z % center.x % center.y % center.z
+ % aoh->getFirstParameter() % aoh->getLastParameter());
+ sg.construction = Sketcher::GeometryFacade::getConstruction(geo);
+ return sg;
+ }},
+ {Part::GeomArcOfParabola::getClassTypeId(),
+ [](const Part::Geometry* geo) {
+ auto aop = static_cast(geo);
+ SingleGeometry sg;
+ auto focus = aop->getFocus();
+ auto axisPoint = aop->getCenter();
+ sg.creation = boost::str(
+ boost::format("Part.ArcOfParabola(Part.Parabola(App.Vector(%f, %f, %f), "
+ "App.Vector(%f, %f, %f), App.Vector(0, 0, 1)), %f, %f)")
+ % focus.x % focus.y % focus.z % axisPoint.x % axisPoint.y % axisPoint.z
+ % aop->getFirstParameter() % aop->getLastParameter());
+ sg.construction = Sketcher::GeometryFacade::getConstruction(geo);
+ return sg;
+ }},
+ {Part::GeomBSplineCurve::getClassTypeId(),
+ [](const Part::Geometry* geo) {
+ auto bSpline = static_cast(geo);
+
+ std::stringstream stream;
+ std::vector poles = bSpline->getPoles();
+ for (auto& pole : poles) {
+ stream << "App.Vector(" << pole.x << "," << pole.y << "),";
+ }
+ std::string controlpoints = stream.str();
+ // remove last comma and add brackets
+ int index = controlpoints.rfind(',');
+ controlpoints.resize(index);
+ controlpoints.insert(0, 1, '[');
+ controlpoints.append(1, ']');
+
+ SingleGeometry sg;
+ sg.creation =
+ boost::str(boost::format("Part.BSplineCurve (%s,None,None,%s,%d,None,False)")
+ % controlpoints.c_str() % (bSpline->isPeriodic() ? "True" : "False")
+ % bSpline->getDegree());
+ sg.construction = Sketcher::GeometryFacade::getConstruction(geo);
+ return sg;
+ }},
{Part::GeomCircle::getClassTypeId(),
[](const Part::Geometry* geo) {
auto circle = static_cast(geo);
@@ -336,21 +433,26 @@ std::string PythonConverter::process(const Sketcher::Constraint* constraint)
}},
{Sketcher::InternalAlignment,
[](const Sketcher::Constraint* constr) {
- if (constr->InternalAlignmentIndex == EllipseMajorDiameter
- || constr->InternalAlignmentIndex == EllipseMinorDiameter) {
+ if (constr->AlignmentType == EllipseMajorDiameter
+ || constr->AlignmentType == EllipseMinorDiameter
+ || constr->AlignmentType == HyperbolaMajor
+ || constr->AlignmentType == HyperbolaMinor
+ || constr->AlignmentType == ParabolaFocalAxis) {
return boost::str(
boost::format("Sketcher.Constraint('InternalAlignment:%s', %i, %i)")
% constr->internalAlignmentTypeToString() % constr->First
% constr->Second);
}
- else if (constr->InternalAlignmentIndex == EllipseFocus1
- || constr->InternalAlignmentIndex == EllipseFocus2) {
+ else if (constr->AlignmentType == EllipseFocus1
+ || constr->AlignmentType == EllipseFocus2
+ || constr->AlignmentType == HyperbolaFocus
+ || constr->AlignmentType == ParabolaFocus) {
return boost::str(
boost::format("Sketcher.Constraint('InternalAlignment:%s', %i, %i, %i)")
% constr->internalAlignmentTypeToString() % constr->First
% static_cast(constr->FirstPos) % constr->Second);
}
- else if (constr->InternalAlignmentIndex == BSplineControlPoint) {
+ else if (constr->AlignmentType == BSplineControlPoint) {
return boost::str(
boost::format(
"Sketcher.Constraint('InternalAlignment:%s', %i, %i, %i, %i)")
@@ -358,6 +460,12 @@ std::string PythonConverter::process(const Sketcher::Constraint* constraint)
% static_cast(constr->FirstPos) % constr->Second
% constr->InternalAlignmentIndex);
}
+ else if (constr->AlignmentType == BSplineKnotPoint) {
+ return boost::str(
+ boost::format("Sketcher.Constraint('InternalAlignment:%s', %i, 1, %i, %i)")
+ % constr->internalAlignmentTypeToString() % constr->First % constr->Second
+ % constr->InternalAlignmentIndex);
+ }
THROWM(Base::ValueError,
"PythonConverter: Constraint Alignment Type not supported")
diff --git a/src/Mod/Sketcher/App/PythonConverter.h b/src/Mod/Sketcher/App/PythonConverter.h
index ae86ffb798..98535583b1 100644
--- a/src/Mod/Sketcher/App/PythonConverter.h
+++ b/src/Mod/Sketcher/App/PythonConverter.h
@@ -43,7 +43,6 @@ class Constraint;
class SketcherExport PythonConverter
{
-
class SingleGeometry
{
public:
@@ -52,14 +51,22 @@ class SketcherExport PythonConverter
};
public:
+ enum class Mode
+ {
+ CreateInternalGeometry,
+ OmitInternalGeometry
+ };
+
explicit PythonConverter() = delete;
~PythonConverter() = delete;
/// Convert a geometry into the string representing the command creating it
- static std::string convert(const Part::Geometry* geo);
+ static std::string convert(const Part::Geometry* geo, Mode mode = Mode::CreateInternalGeometry);
/// Convert a vector of geometries into the string representing the command creating them
- static std::string convert(const std::string& doc, const std::vector& geos);
+ static std::string convert(const std::string& doc,
+ const std::vector& geos,
+ Mode mode = Mode::CreateInternalGeometry);
static std::string convert(const Sketcher::Constraint* constraint);
diff --git a/src/Mod/Sketcher/Gui/DrawSketchDefaultHandler.h b/src/Mod/Sketcher/Gui/DrawSketchDefaultHandler.h
index 88e706d282..580781019d 100644
--- a/src/Mod/Sketcher/Gui/DrawSketchDefaultHandler.h
+++ b/src/Mod/Sketcher/Gui/DrawSketchDefaultHandler.h
@@ -1110,11 +1110,12 @@ protected:
void commandAddShapeGeometryAndConstraints()
{
auto shapeGeometry = toPointerVector(ShapeGeometry);
- Gui::Command::doCommand(
- Gui::Command::Doc,
- Sketcher::PythonConverter::convert(Gui::Command::getObjectCmd(sketchgui->getObject()),
- shapeGeometry)
- .c_str());
+ Gui::Command::doCommand(Gui::Command::Doc,
+ Sketcher::PythonConverter::convert(
+ Gui::Command::getObjectCmd(sketchgui->getObject()),
+ shapeGeometry,
+ Sketcher::PythonConverter::Mode::OmitInternalGeometry)
+ .c_str());
auto shapeConstraints = toPointerVector(ShapeConstraints);
Gui::Command::doCommand(