From 359e9a5d78f25e73fb4bdd7c2aa3080b26ca7aba Mon Sep 17 00:00:00 2001 From: Abdullah Tahiri Date: Thu, 16 Jul 2015 05:00:00 +0200 Subject: [PATCH] Sketcher: New Feature: Symmetric tool ===================================== Creates geometry symmetric to the selected one with respect to the last selected point or line. It does not support the following constraints: - Angle - Horiz/vertical distance - SnellLaw Horizontal and Vertical (directional) dimensions removed from the the copying process when created on a single edge (by picking one edge instead of two points), i.e. Constraint involving only one GeoId at "First". --- src/Mod/Sketcher/App/SketchObject.cpp | 392 ++++++++++++++++++ src/Mod/Sketcher/App/SketchObject.h | 2 + src/Mod/Sketcher/App/SketchObjectPy.xml | 5 + src/Mod/Sketcher/App/SketchObjectPyImp.cpp | 38 ++ src/Mod/Sketcher/Gui/CommandSketcherTools.cpp | 199 +++++++++ src/Mod/Sketcher/Gui/Workbench.cpp | 6 +- 6 files changed, 640 insertions(+), 2 deletions(-) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index f729739fb..2e8ea27bc 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 cb7c3a583..d5554c66c 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 e93dcbdc8..30877b1b9 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 c51b9997d..46c2beaa7 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 329890b7c..04070b449 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 f1141944a..e359902d5 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