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(