diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp
index f729739fb8..2e8ea27bce 100644
--- a/src/Mod/Sketcher/App/SketchObject.cpp
+++ b/src/Mod/Sketcher/App/SketchObject.cpp
@@ -50,6 +50,7 @@
#include
#include
#include
+#include
#include
@@ -1613,6 +1614,397 @@ int SketchObject::trim(int GeoId, const Base::Vector3d& point)
return -1;
}
+int SketchObject::addSymmetric(const std::vector &geoIdList, int refGeoId, Sketcher::PointPos refPosId/*=Sketcher::none*/)
+{
+ const std::vector< Part::Geometry * > &geovals = getInternalGeometry();
+ std::vector< Part::Geometry * > newgeoVals(geovals);
+
+ const std::vector< Constraint * > &constrvals = this->Constraints.getValues();
+ std::vector< Constraint * > newconstrVals(constrvals);
+
+ int cgeoid = getHighestCurveIndex()+1;
+
+ std::map geoIdMap;
+ std::map isStartEndInverted;
+
+ // reference is a line
+ if(refPosId == Sketcher::none)
+ {
+ const Part::Geometry *georef = getGeometry(refGeoId);
+ if(georef->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
+ Base::Console().Error("Reference for symmetric is neither a point nor a line.\n");
+ return -1;
+ }
+
+ const Part::GeomLineSegment *refGeoLine = static_cast(georef);
+ //line
+ Base::Vector3d refstart = refGeoLine->getStartPoint();
+ Base::Vector3d vectline = refGeoLine->getEndPoint()-refstart;
+
+ for (std::vector::const_iterator it = geoIdList.begin(); it != geoIdList.end(); ++it) {
+ const Part::Geometry *geo = getGeometry(*it);
+ Part::Geometry *geosym = geo->clone();
+
+ // Handle Geometry
+ if(geosym->getTypeId() == Part::GeomLineSegment::getClassTypeId()){
+ Part::GeomLineSegment *geosymline = static_cast(geosym);
+ Base::Vector3d sp = geosymline->getStartPoint();
+ Base::Vector3d ep = geosymline->getEndPoint();
+
+
+ geosymline->setPoints(sp+2.0*(sp.Perpendicular(refGeoLine->getStartPoint(),vectline)-sp),
+ ep+2.0*(ep.Perpendicular(refGeoLine->getStartPoint(),vectline)-ep));
+ isStartEndInverted.insert(std::make_pair(*it, false));
+ }
+ else if(geosym->getTypeId() == Part::GeomCircle::getClassTypeId()){
+ Part::GeomCircle *geosymcircle = static_cast(geosym);
+ Base::Vector3d cp = geosymcircle->getCenter();
+
+ geosymcircle->setCenter(cp+2.0*(cp.Perpendicular(refGeoLine->getStartPoint(),vectline)-cp));
+ isStartEndInverted.insert(std::make_pair(*it, false));
+ }
+ else if(geosym->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){
+ Part::GeomArcOfCircle *geoaoc = static_cast(geosym);
+ Base::Vector3d sp = geoaoc->getStartPoint(true);
+ Base::Vector3d ep = geoaoc->getEndPoint(true);
+ Base::Vector3d cp = geoaoc->getCenter();
+
+ Base::Vector3d ssp = sp+2.0*(sp.Perpendicular(refGeoLine->getStartPoint(),vectline)-sp);
+ Base::Vector3d sep = ep+2.0*(ep.Perpendicular(refGeoLine->getStartPoint(),vectline)-ep);
+ Base::Vector3d scp = cp+2.0*(cp.Perpendicular(refGeoLine->getStartPoint(),vectline)-cp);
+
+ double theta1 = Base::fmod(atan2(sep.y - scp.y, sep.x - scp.x), 2.f*M_PI);
+ double theta2 = Base::fmod(atan2(ssp.y - scp.y, ssp.x - scp.x), 2.f*M_PI);
+
+ geoaoc->setCenter(scp);
+ geoaoc->setRange(theta1,theta2,true);
+ isStartEndInverted.insert(std::make_pair(*it, true));
+ }
+ else if(geosym->getTypeId() == Part::GeomEllipse::getClassTypeId()){
+ Part::GeomEllipse *geosymellipse = static_cast(geosym);
+ Base::Vector3d cp = geosymellipse->getCenter();
+
+ Base::Vector3d majdir = geosymellipse->getMajorAxisDir();
+ double majord=geosymellipse->getMajorRadius();
+ double minord=geosymellipse->getMinorRadius();
+ double df= sqrt(majord*majord-minord*minord);
+ Base::Vector3d f1 = cp + df * majdir;
+
+ Base::Vector3d sf1 = f1+2.0*(f1.Perpendicular(refGeoLine->getStartPoint(),vectline)-f1);
+ Base::Vector3d scp = cp+2.0*(cp.Perpendicular(refGeoLine->getStartPoint(),vectline)-cp);
+
+ geosymellipse->setMajorAxisDir(sf1-scp);
+
+ geosymellipse->setCenter(scp);
+ isStartEndInverted.insert(std::make_pair(*it, false));
+ }
+ else if(geosym->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){
+ Part::GeomArcOfEllipse *geosymaoe = static_cast(geosym);
+ Base::Vector3d cp = geosymaoe->getCenter();
+ Base::Vector3d sp = geosymaoe->getStartPoint(true);
+ Base::Vector3d ep = geosymaoe->getEndPoint(true);
+
+ Base::Vector3d majdir = geosymaoe->getMajorAxisDir();
+ double majord=geosymaoe->getMajorRadius();
+ double minord=geosymaoe->getMinorRadius();
+ double df= sqrt(majord*majord-minord*minord);
+ Base::Vector3d f1 = cp + df * majdir;
+
+ Base::Vector3d sf1 = f1+2.0*(f1.Perpendicular(refGeoLine->getStartPoint(),vectline)-f1);
+ Base::Vector3d scp = cp+2.0*(cp.Perpendicular(refGeoLine->getStartPoint(),vectline)-cp);
+ Base::Vector3d ssp = sp+2.0*(sp.Perpendicular(refGeoLine->getStartPoint(),vectline)-sp);
+ Base::Vector3d sep = ep+2.0*(ep.Perpendicular(refGeoLine->getStartPoint(),vectline)-ep);
+
+ geosymaoe->setMajorAxisDir(sf1-scp);
+
+ geosymaoe->setCenter(scp);
+
+ double theta1,theta2;
+ geosymaoe->closestParameter(sep,theta1);
+ geosymaoe->closestParameter(ssp,theta2);
+
+ geosymaoe->setRange(theta1,theta2,true);
+ isStartEndInverted.insert(std::make_pair(*it, true));
+ }
+ else if(geosym->getTypeId() == Part::GeomPoint::getClassTypeId()){
+ Part::GeomPoint *geosympoint = static_cast(geosym);
+ Base::Vector3d cp = geosympoint->getPoint();
+
+ geosympoint->setPoint(cp+2.0*(cp.Perpendicular(refGeoLine->getStartPoint(),vectline)-cp));
+ isStartEndInverted.insert(std::make_pair(*it, false));
+ }
+ else {
+ Base::Console().Error("Unsupported Geometry!! Just copying it.\n");
+ isStartEndInverted.insert(std::make_pair(*it, false));
+ }
+
+ newgeoVals.push_back(geosym);
+ geoIdMap.insert(std::make_pair(*it, cgeoid));
+ cgeoid++;
+ }
+
+ }
+ else //reference is a point
+ {
+ Vector3d refpoint;
+ const Part::Geometry *georef = getGeometry(refGeoId);
+
+ if (georef->getTypeId() == Part::GeomPoint::getClassTypeId()) {
+ refpoint = static_cast(georef)->getPoint();
+ }
+ else if ( refGeoId == -1 && refPosId == Sketcher::start) {
+ refpoint = Vector3d(0,0,0);
+ }
+ else {
+ switch(refPosId){
+ case Sketcher::start:
+ if(georef->getTypeId() == Part::GeomLineSegment::getClassTypeId()){
+ const Part::GeomLineSegment *geosymline = static_cast(georef);
+ refpoint = geosymline->getStartPoint();
+ }
+ else if(georef->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){
+ const Part::GeomArcOfCircle *geoaoc = static_cast(georef);
+ refpoint = geoaoc->getStartPoint(true);
+ }
+ else if(georef->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){
+ const Part::GeomArcOfEllipse *geosymaoe = static_cast(georef);
+ refpoint = geosymaoe->getStartPoint(true);
+ }
+ break;
+ case Sketcher::end:
+ if(georef->getTypeId() == Part::GeomLineSegment::getClassTypeId()){
+ const Part::GeomLineSegment *geosymline = static_cast(georef);
+ refpoint = geosymline->getEndPoint();
+ }
+ else if(georef->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){
+ const Part::GeomArcOfCircle *geoaoc = static_cast(georef);
+ refpoint = geoaoc->getEndPoint(true);
+ }
+ else if(georef->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){
+ const Part::GeomArcOfEllipse *geosymaoe = static_cast(georef);
+ refpoint = geosymaoe->getEndPoint(true);
+ }
+ break;
+ case Sketcher::mid:
+ if(georef->getTypeId() == Part::GeomCircle::getClassTypeId()){
+ const Part::GeomCircle *geosymcircle = static_cast(georef);
+ refpoint = geosymcircle->getCenter();
+ }
+ else if(georef->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){
+ const Part::GeomArcOfCircle *geoaoc = static_cast(georef);
+ refpoint = geoaoc->getCenter();
+ }
+ else if(georef->getTypeId() == Part::GeomEllipse::getClassTypeId()){
+ const Part::GeomEllipse *geosymellipse = static_cast(georef);
+ refpoint = geosymellipse->getCenter();
+ }
+ else if(georef->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){
+ const Part::GeomArcOfEllipse *geosymaoe = static_cast(georef);
+ refpoint = geosymaoe->getCenter();
+ }
+ break;
+ default:
+ Base::Console().Error("Wrong PointPosId.\n");
+ return -1;
+ }
+ }
+
+ for (std::vector::const_iterator it = geoIdList.begin(); it != geoIdList.end(); ++it) {
+ const Part::Geometry *geo = getGeometry(*it);
+ Part::Geometry *geosym = geo->clone();
+
+ // Handle Geometry
+ if(geosym->getTypeId() == Part::GeomLineSegment::getClassTypeId()){
+ Part::GeomLineSegment *geosymline = static_cast(geosym);
+ Base::Vector3d sp = geosymline->getStartPoint();
+ Base::Vector3d ep = geosymline->getEndPoint();
+ Base::Vector3d ssp = sp + 2.0*(refpoint-sp);
+ Base::Vector3d sep = ep + 2.0*(refpoint-ep);
+
+ geosymline->setPoints(ssp, sep);
+ isStartEndInverted.insert(std::make_pair(*it, false));
+ }
+ else if(geosym->getTypeId() == Part::GeomCircle::getClassTypeId()){
+ Part::GeomCircle *geosymcircle = static_cast(geosym);
+ Base::Vector3d cp = geosymcircle->getCenter();
+
+ geosymcircle->setCenter(cp + 2.0*(refpoint-cp));
+ isStartEndInverted.insert(std::make_pair(*it, false));
+ }
+ else if(geosym->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){
+ Part::GeomArcOfCircle *geoaoc = static_cast(geosym);
+ Base::Vector3d sp = geoaoc->getStartPoint(true);
+ Base::Vector3d ep = geoaoc->getEndPoint(true);
+ Base::Vector3d cp = geoaoc->getCenter();
+
+ Base::Vector3d ssp = sp + 2.0*(refpoint-sp);
+ Base::Vector3d sep = ep + 2.0*(refpoint-ep);
+ Base::Vector3d scp = cp + 2.0*(refpoint-cp);
+
+ double theta1 = Base::fmod(atan2(ssp.y - scp.y, ssp.x - scp.x), 2.f*M_PI);
+ double theta2 = Base::fmod(atan2(sep.y - scp.y, sep.x - scp.x), 2.f*M_PI);
+
+ geoaoc->setCenter(scp);
+ geoaoc->setRange(theta1,theta2,true);
+ isStartEndInverted.insert(std::make_pair(*it, false));
+ }
+ else if(geosym->getTypeId() == Part::GeomEllipse::getClassTypeId()){
+ Part::GeomEllipse *geosymellipse = static_cast(geosym);
+ Base::Vector3d cp = geosymellipse->getCenter();
+
+ Base::Vector3d majdir = geosymellipse->getMajorAxisDir();
+ double majord=geosymellipse->getMajorRadius();
+ double minord=geosymellipse->getMinorRadius();
+ double df= sqrt(majord*majord-minord*minord);
+ Base::Vector3d f1 = cp + df * majdir;
+
+ Base::Vector3d sf1 = f1 + 2.0*(refpoint-f1);
+ Base::Vector3d scp = cp + 2.0*(refpoint-cp);
+
+ geosymellipse->setMajorAxisDir(sf1-scp);
+
+ geosymellipse->setCenter(scp);
+ isStartEndInverted.insert(std::make_pair(*it, false));
+ }
+ else if(geosym->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){
+ Part::GeomArcOfEllipse *geosymaoe = static_cast(geosym);
+ Base::Vector3d cp = geosymaoe->getCenter();
+ Base::Vector3d sp = geosymaoe->getStartPoint(true);
+ Base::Vector3d ep = geosymaoe->getEndPoint(true);
+
+ Base::Vector3d majdir = geosymaoe->getMajorAxisDir();
+ double majord=geosymaoe->getMajorRadius();
+ double minord=geosymaoe->getMinorRadius();
+ double df= sqrt(majord*majord-minord*minord);
+ Base::Vector3d f1 = cp + df * majdir;
+
+ Base::Vector3d sf1 = f1 + 2.0*(refpoint-f1);
+ Base::Vector3d scp = cp + 2.0*(refpoint-cp);
+ Base::Vector3d ssp = sp + 2.0*(refpoint-sp);
+ Base::Vector3d sep = ep + 2.0*(refpoint-ep);
+
+ geosymaoe->setMajorAxisDir(sf1-scp);
+
+ geosymaoe->setCenter(scp);
+
+ double theta1,theta2;
+ geosymaoe->closestParameter(ssp,theta1);
+ geosymaoe->closestParameter(sep,theta2);
+
+ geosymaoe->setRange(theta1,theta2,true);
+ isStartEndInverted.insert(std::make_pair(*it, false));
+ }
+ else if(geosym->getTypeId() == Part::GeomPoint::getClassTypeId()){
+ Part::GeomPoint *geosympoint = static_cast(geosym);
+ Base::Vector3d cp = geosympoint->getPoint();
+
+ geosympoint->setPoint(cp + 2.0*(refpoint-cp));
+ isStartEndInverted.insert(std::make_pair(*it, false));
+ }
+ else {
+ Base::Console().Error("Unsupported Geometry!! Just copying it.\n");
+ isStartEndInverted.insert(std::make_pair(*it, false));
+ }
+
+ newgeoVals.push_back(geosym);
+ geoIdMap.insert(std::make_pair(*it, cgeoid));
+ cgeoid++;
+ }
+ }
+
+ for (std::vector::const_iterator it = constrvals.begin(); it != constrvals.end(); ++it) {
+
+ std::vector::const_iterator fit=std::find(geoIdList.begin(), geoIdList.end(), (*it)->First);
+
+ if(fit != geoIdList.end()) { // if First of constraint is in geoIdList
+
+ if( (*it)->Second == Constraint::GeoUndef /*&& (*it)->Third == Constraint::GeoUndef*/) {
+ if((*it)->Type != Sketcher::DistanceX &&
+ (*it)->Type != Sketcher::DistanceY) {
+
+ Constraint *constNew = (*it)->clone();
+ constNew->First = geoIdMap[(*it)->First];
+ newconstrVals.push_back(constNew);
+ }
+ }
+ else { // other geoids intervene in this constraint
+
+ std::vector::const_iterator sit=std::find(geoIdList.begin(), geoIdList.end(), (*it)->Second);
+
+ if(sit != geoIdList.end()) { // Second is also in the list
+
+ if( (*it)->Third == Constraint::GeoUndef ) {
+ if((*it)->Type == Sketcher::Coincident ||
+ (*it)->Type == Sketcher::Perpendicular ||
+ (*it)->Type == Sketcher::Parallel ||
+ (*it)->Type == Sketcher::Tangent ||
+ (*it)->Type == Sketcher::Distance ||
+ (*it)->Type == Sketcher::Equal ||
+ (*it)->Type == Sketcher::Radius ||
+ (*it)->Type == Sketcher::PointOnObject ){
+ Constraint *constNew = (*it)->clone();
+ constNew->First = geoIdMap[(*it)->First];
+ constNew->Second = geoIdMap[(*it)->Second];
+ if(isStartEndInverted[(*it)->First]){
+ if((*it)->FirstPos == Sketcher::start)
+ constNew->FirstPos = Sketcher::end;
+ else if((*it)->FirstPos == Sketcher::end)
+ constNew->FirstPos = Sketcher::start;
+ }
+ if(isStartEndInverted[(*it)->Second]){
+ if((*it)->SecondPos == Sketcher::start)
+ constNew->SecondPos = Sketcher::end;
+ else if((*it)->SecondPos == Sketcher::end)
+ constNew->SecondPos = Sketcher::start;
+ }
+ newconstrVals.push_back(constNew);
+ }
+ }
+ else {
+ std::vector::const_iterator tit=std::find(geoIdList.begin(), geoIdList.end(), (*it)->Third);
+
+ if(tit != geoIdList.end()) { // Third is also in the list
+ Constraint *constNew = (*it)->clone();
+ constNew->First = geoIdMap[(*it)->First];
+ constNew->Second = geoIdMap[(*it)->Second];
+ constNew->Third = geoIdMap[(*it)->Third];
+ if(isStartEndInverted[(*it)->First]){
+ if((*it)->FirstPos == Sketcher::start)
+ constNew->FirstPos = Sketcher::end;
+ else if((*it)->FirstPos == Sketcher::end)
+ constNew->FirstPos = Sketcher::start;
+ }
+ if(isStartEndInverted[(*it)->Second]){
+ if((*it)->SecondPos == Sketcher::start)
+ constNew->SecondPos = Sketcher::end;
+ else if((*it)->SecondPos == Sketcher::end)
+ constNew->SecondPos = Sketcher::start;
+ }
+ if(isStartEndInverted[(*it)->Third]){
+ if((*it)->ThirdPos == Sketcher::start)
+ constNew->ThirdPos = Sketcher::end;
+ else if((*it)->ThirdPos == Sketcher::end)
+ constNew->ThirdPos = Sketcher::start;
+ }
+ newconstrVals.push_back(constNew);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Geometry.setValues(newgeoVals);
+ Constraints.acceptGeometry(getCompleteGeometry());
+ rebuildVertexIndex();
+
+ if( newconstrVals.size() > constrvals.size() )
+ Constraints.setValues(newconstrVals);
+
+ return Geometry.getSize()-1;
+}
+
+
int SketchObject::ExposeInternalGeometry(int GeoId)
{
if (GeoId < 0 || GeoId > getHighestCurveIndex())
diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h
index cb7c3a5830..d5554c66c0 100644
--- a/src/Mod/Sketcher/App/SketchObject.h
+++ b/src/Mod/Sketcher/App/SketchObject.h
@@ -150,6 +150,8 @@ public:
/// trim a curve
int trim(int geoId, const Base::Vector3d& point);
+ /// adds symmetric geometric elements with respect to the refGeoId (line or point)
+ int addSymmetric(const std::vector &geoIdList, int refGeoId, Sketcher::PointPos refPosId=Sketcher::none);
/// Exposes all internal geometry of an object supporting internal geometry
/*!
* \return -1 on error
diff --git a/src/Mod/Sketcher/App/SketchObjectPy.xml b/src/Mod/Sketcher/App/SketchObjectPy.xml
index e93dcbdc8e..30877b1b97 100644
--- a/src/Mod/Sketcher/App/SketchObjectPy.xml
+++ b/src/Mod/Sketcher/App/SketchObjectPy.xml
@@ -132,6 +132,11 @@
trim a curve with a given id at a given reference point
+
+
+ add a symmetric geometric objects to the sketch with respect to a reference point or line
+
+
Exposes all internal geometry of an object supporting internal geometry
diff --git a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp
index c51b9997d2..46c2beaa76 100644
--- a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp
+++ b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp
@@ -754,6 +754,44 @@ PyObject* SketchObjectPy::trim(PyObject *args)
Py_Return;
}
+PyObject* SketchObjectPy::addSymmetric(PyObject *args)
+{
+ PyObject *pcObj;
+ int refGeoId;
+ int refPosId = Sketcher::none;
+ if (!PyArg_ParseTuple(args, "Oi|i", &pcObj, &refGeoId, &refPosId))
+ return 0;
+
+ if (PyObject_TypeCheck(pcObj, &(PyList_Type)) ||
+ PyObject_TypeCheck(pcObj, &(PyTuple_Type))) {
+ std::vector geoIdList;
+ Py::Sequence list(pcObj);
+ for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) {
+ if (PyInt_Check((*it).ptr()))
+ geoIdList.push_back(PyInt_AsLong((*it).ptr()));
+ }
+
+ int ret = this->getSketchObjectPtr()->addSymmetric(geoIdList,refGeoId,(Sketcher::PointPos) refPosId) + 1;
+
+ if(ret == -1)
+ throw Py::TypeError("Symmetric operation unsuccessful!");
+
+ std::size_t numGeo = geoIdList.size();
+ Py::Tuple tuple(numGeo);
+ for (std::size_t i=0; iob_type->tp_name;
+ throw Py::TypeError(error);
+}
+
+
PyObject* SketchObjectPy::calculateAngleViaPoint(PyObject *args)
{
int GeoId1=0, GeoId2=0;
diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp
index 329890b7ca..04070b449a 100644
--- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp
+++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp
@@ -28,6 +28,7 @@
# include
#endif
+#include
#include
#include
#include
@@ -971,6 +972,203 @@ bool CmdSketcherRestoreInternalAlignmentGeometry::isActive(void)
return isSketcherAcceleratorActive( getActiveGuiDocument(), true );
}
+DEF_STD_CMD_A(CmdSketcherSymmetry);
+
+CmdSketcherSymmetry::CmdSketcherSymmetry()
+ :Command("Sketcher_Symmetry")
+{
+ sAppModule = "Sketcher";
+ sGroup = QT_TR_NOOP("Sketcher");
+ sMenuText = QT_TR_NOOP("Symmetry");
+ sToolTipText = QT_TR_NOOP("Creates symmetric geometry with respect to the last selected line or point");
+ sWhatsThis = sToolTipText;
+ sStatusTip = sToolTipText;
+ sPixmap = "Sketcher_Symmetry";
+ sAccel = "";
+ eType = ForEdit;
+}
+
+void CmdSketcherSymmetry::activated(int iMsg)
+{
+ // get the selection
+ std::vector selection = getSelection().getSelectionEx();
+ Sketcher::SketchObject* Obj = dynamic_cast(selection[0].getObject());
+
+ // only one sketch with its subelements are allowed to be selected
+ if (selection.size() != 1) {
+ QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
+ QObject::tr("Select elements from a single sketch."));
+ return;
+ }
+
+ // get the needed lists and objects
+ const std::vector &SubNames = selection[0].getSubNames();
+ const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
+
+ std::string doc_name = Obj->getDocument()->getName();
+ std::string obj_name = Obj->getNameInDocument();
+ std::stringstream ss;
+
+ getSelection().clearSelection();
+
+ int nelements = SubNames.size();
+
+ int LastGeoId;
+ Sketcher::PointPos LastPointPos = Sketcher::none;
+ const Part::Geometry *LastGeo;
+ typedef enum { invalid = -1, line = 0, point = 1 } GeoType;
+
+ GeoType lastgeotype = invalid;
+
+ // create python command with list of elements
+ std::stringstream stream;
+ int geoids = 0;
+
+ for (std::vector::const_iterator it=SubNames.begin(); it != SubNames.end(); ++it) {
+ // only handle non-external edges
+ if ((it->size() > 4 && it->substr(0,4) == "Edge") ||
+ (it->size() > 12 && it->substr(0,12) == "ExternalEdge")) {
+
+ if(it->substr(0,4) == "Edge") {
+ LastGeoId = std::atoi(it->substr(4,4000).c_str()) - 1;
+ LastPointPos = Sketcher::none;
+ }
+ else {
+ LastGeoId = -std::atoi(it->substr(12,4000).c_str()) - 2;
+ LastPointPos = Sketcher::none;
+ }
+
+ // reference can be external or non-external
+ LastGeo = Obj->getGeometry(LastGeoId);
+ // Only for supported types
+ if(LastGeo->getTypeId() == Part::GeomLineSegment::getClassTypeId())
+ lastgeotype = line;
+ else
+ lastgeotype = invalid;
+
+ // lines to make symmetric (only non-external)
+ if(LastGeoId>=0) {
+ geoids++;
+ stream << LastGeoId << ",";
+ }
+ }
+ else if(it->size() > 6 && it->substr(0,6) == "Vertex"){
+ // only if it is a GeomPoint
+ int VtId = std::atoi(it->substr(6,4000).c_str()) - 1;
+ int GeoId;
+ Sketcher::PointPos PosId;
+ Obj->getGeoVertexIndex(VtId, GeoId, PosId);
+ if (Obj->getGeometry(GeoId)->getTypeId() == Part::GeomPoint::getClassTypeId()) {
+ LastGeoId = GeoId;
+ LastPointPos = Sketcher::start;
+ lastgeotype = point;
+
+ // points to make symmetric
+ if(LastGeoId>=0) {
+ geoids++;
+ stream << LastGeoId << ",";
+ }
+ }
+ }
+ }
+
+ bool lastvertexoraxis=false;
+ // check if last selected element is a Vertex, not being a GeomPoint
+ if(SubNames.rbegin()->size() > 6 && SubNames.rbegin()->substr(0,6) == "Vertex"){
+ int VtId = std::atoi(SubNames.rbegin()->substr(6,4000).c_str()) - 1;
+ int GeoId;
+ Sketcher::PointPos PosId;
+ Obj->getGeoVertexIndex(VtId, GeoId, PosId);
+ if (Obj->getGeometry(GeoId)->getTypeId() != Part::GeomPoint::getClassTypeId()) {
+ LastGeoId = GeoId;
+ LastPointPos = PosId;
+ lastgeotype = point;
+ lastvertexoraxis=true;
+ }
+ }
+ // check if last selected element is horizontal axis
+ else if(SubNames.rbegin()->size() == 6 && SubNames.rbegin()->substr(0,6) == "H_Axis"){
+ LastGeoId = -1;
+ LastPointPos = Sketcher::none;
+ lastgeotype = line;
+ lastvertexoraxis=true;
+ }
+ // check if last selected element is vertical axis
+ else if(SubNames.rbegin()->size() == 6 && SubNames.rbegin()->substr(0,6) == "V_Axis"){
+ LastGeoId = -2;
+ LastPointPos = Sketcher::none;
+ lastgeotype = line;
+ lastvertexoraxis=true;
+ }
+ // check if last selected element is the root point
+ else if(SubNames.rbegin()->size() == 9 && SubNames.rbegin()->substr(0,9) == "RootPoint"){
+ LastGeoId = -1;
+ LastPointPos = Sketcher::start;
+ lastgeotype = point;
+ lastvertexoraxis=true;
+ }
+
+ if ( geoids < 2 || (geoids<1 && LastGeoId<0) || (geoids<1 && lastvertexoraxis) ) {
+ QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
+ QObject::tr("A symmetric construction requires at least two geometric elements, the last geometric element being the reference for the symmetry construction."));
+ return;
+ }
+
+ if ( lastgeotype == invalid ) {
+ QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
+ QObject::tr("The last element must be a point or a line serving as reference for the symmetry construction."));
+ return;
+ }
+
+ std::string geoIdList = stream.str();
+
+ // missing cases:
+ // 1- Last element is an edge, and is V or H axis
+ // 2- Last element is a point GeomPoint
+ // 3- Last element is a point (Vertex)
+
+ if(LastGeoId>=0 && !lastvertexoraxis) {
+ // if LastGeoId was added remove the last element
+ int index = geoIdList.rfind(',');
+ index = geoIdList.rfind(',',index-1);
+ geoIdList.resize(index);
+ }
+ else {
+ int index = geoIdList.rfind(',');
+ geoIdList.resize(index);
+ }
+
+ geoIdList.insert(0,1,'[');
+ geoIdList.append(1,']');
+
+ Gui::Command::openCommand("Create Symmetric geometry");
+
+ ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
+ bool autoRecompute = hGrp->GetBool("AutoRecompute",false);
+
+ try{
+ Gui::Command::doCommand(
+ Gui::Command::Doc, "App.ActiveDocument.%s.addSymmetric(%s,%d,%d)",
+ Obj->getNameInDocument(), geoIdList.c_str(), LastGeoId, LastPointPos
+ );
+
+ Gui::Command::commitCommand();
+ }
+ catch (const Base::Exception& e) {
+ Base::Console().Error("%s\n", e.what());
+ Gui::Command::abortCommand();
+ }
+
+ if(autoRecompute)
+ Gui::Command::updateActive();
+ else
+ Obj->solve();
+}
+
+bool CmdSketcherSymmetry::isActive(void)
+{
+ return isSketcherAcceleratorActive( getActiveGuiDocument(), true );
+}
void CreateSketcherCommandsConstraintAccel(void)
{
@@ -986,4 +1184,5 @@ void CreateSketcherCommandsConstraintAccel(void)
rcCmdMgr.addCommand(new CmdSketcherSelectConflictingConstraints());
rcCmdMgr.addCommand(new CmdSketcherSelectElementsAssociatedWithConstraints());
rcCmdMgr.addCommand(new CmdSketcherRestoreInternalAlignmentGeometry());
+ rcCmdMgr.addCommand(new CmdSketcherSymmetry());
}
diff --git a/src/Mod/Sketcher/Gui/Workbench.cpp b/src/Mod/Sketcher/Gui/Workbench.cpp
index f1141944a5..e359902d5c 100644
--- a/src/Mod/Sketcher/Gui/Workbench.cpp
+++ b/src/Mod/Sketcher/Gui/Workbench.cpp
@@ -245,14 +245,16 @@ inline void SketcherAddWorkbenchTools(Gui::MenuItem& consaccel){
<< "Sketcher_SelectRedundantConstraints"
<< "Sketcher_SelectConflictingConstraints"
<< "Sketcher_SelectElementsAssociatedWithConstraints"
- << "Sketcher_RestoreInternalAlignmentGeometry";
+ << "Sketcher_RestoreInternalAlignmentGeometry"
+ << "Sketcher_Symmetry";
}
template <>
inline void SketcherAddWorkbenchTools(Gui::ToolBarItem& consaccel){
consaccel << "Sketcher_CloseShape"
<< "Sketcher_ConnectLines"
<< "Sketcher_SelectConstraints"
- << "Sketcher_RestoreInternalAlignmentGeometry";
+ << "Sketcher_RestoreInternalAlignmentGeometry"
+ << "Sketcher_Symmetry";
}
template