From 7b053b5602064a41b2e33ff9b830589e2a325848 Mon Sep 17 00:00:00 2001 From: Abdullah Tahiri Date: Wed, 5 Aug 2015 14:45:27 +0200 Subject: [PATCH] Sketcher: UI Copy Support & 2D Array python command rework ========================================================== - Support for copying geometric elements in the sketcher with Ctrl+C (or using the still missing icon). It will show you the vector of displacement from the "reference point". The reference point can be chosen by the user (although it is not necessary to do it so) by making the point the user wish to be the reference point the last selected element. It conveniently incorporates "autoconstraints", so that you can make this point (the one of the copy) directly coincident with any other point in the sketch. - Python 2D array command modified to lock elements position using construction lines and constraints. - Support for different spacing between u and v directions (the direction of the cols and the direction of the rows). - Support to avoid copying DistanceX and DistanceY constraints when used for locking a point. This means that if the geometry that you copy(array) is fully constraint, the resulting 2D array is also fully constraint. - UI support for creating 2D linear arrays in the sketcher. - Bug fix in python addArray, wrong line copy startingpoint calculation fixed. How to create a 2D array in the sketcher: 1. Select your geometric elements. 2. Click the button 3. Fill in the rows/cols and preferences on spacing and constraining each element of the array 4. Click Ok 5. Define the direction of the cols of the array and click --- src/Mod/Sketcher/App/SketchObject.cpp | 277 +++++++- src/Mod/Sketcher/App/SketchObject.h | 6 +- src/Mod/Sketcher/App/SketchObjectPyImp.cpp | 8 +- src/Mod/Sketcher/Gui/CMakeLists.txt | 4 + src/Mod/Sketcher/Gui/CommandSketcherTools.cpp | 594 +++++++++++++++++- .../Sketcher/Gui/SketchLinearArrayDialog.cpp | 78 +++ .../Sketcher/Gui/SketchLinearArrayDialog.h | 55 ++ .../Sketcher/Gui/SketchLinearArrayDialog.ui | 187 ++++++ src/Mod/Sketcher/Gui/Workbench.cpp | 9 +- 9 files changed, 1178 insertions(+), 40 deletions(-) create mode 100644 src/Mod/Sketcher/Gui/SketchLinearArrayDialog.cpp create mode 100644 src/Mod/Sketcher/Gui/SketchLinearArrayDialog.h create mode 100644 src/Mod/Sketcher/Gui/SketchLinearArrayDialog.ui diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 28579d8c50..429b0df826 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -2011,7 +2011,8 @@ int SketchObject::addSymmetric(const std::vector &geoIdList, int refGeoId, return Geometry.getSize()-1; } -int SketchObject::addCopy(const std::vector &geoIdList, const Base::Vector3d& displacement, int csize/*=2*/, int rsize/*=1*/) +int SketchObject::addCopy(const std::vector &geoIdList, const Base::Vector3d& displacement, int csize/*=2*/, int rsize/*=1*/, + bool constraindisplacement /*= false*/, double perpscale /*= 1.0*/) { const std::vector< Part::Geometry * > &geovals = getInternalGeometry(); std::vector< Part::Geometry * > newgeoVals(geovals); @@ -2021,83 +2022,142 @@ int SketchObject::addCopy(const std::vector &geoIdList, const Base::Vector3 int cgeoid = getHighestCurveIndex()+1; + int iterfirstgeoid = -1 ; + + Base::Vector3d iterfirstpoint; + + int refgeoid = -1; + + int colrefgeoid = 0, rowrefgeoid = 0; + + int currentrowfirstgeoid= -1, prevrowstartfirstgeoid = -1, prevfirstgeoid = -1; + + Sketcher::PointPos refposId; + std::map geoIdMap; - Base::Vector3d perpendicularDisplacement = Base::Vector3d(displacement.y,-displacement.x,0); + Base::Vector3d perpendicularDisplacement = Base::Vector3d(perpscale*displacement.y,perpscale*-displacement.x,0); int x,y; - for (y=0;ygetTypeId() == Part::GeomCircle::getClassTypeId() || + geo->getTypeId() == Part::GeomEllipse::getClassTypeId() ){ + refposId = Sketcher::mid; + } + else + refposId = Sketcher::start; + continue; // the first element is already in place + } + else { + prevfirstgeoid = iterfirstgeoid; + + iterfirstgeoid = cgeoid; + + if( x == 0 ) { // if first element of second row + prevrowstartfirstgeoid = currentrowfirstgeoid; + currentrowfirstgeoid = cgeoid; + } + } for (std::vector::const_iterator it = geoIdList.begin(); it != geoIdList.end(); ++it) { const Part::Geometry *geo = getGeometry(*it); - Part::Geometry *geosym = geo->clone(); + Part::Geometry *geocopy = geo->clone(); // Handle Geometry - if(geosym->getTypeId() == Part::GeomLineSegment::getClassTypeId()){ - Part::GeomLineSegment *geosymline = static_cast(geosym); + if(geocopy->getTypeId() == Part::GeomLineSegment::getClassTypeId()){ + Part::GeomLineSegment *geosymline = static_cast(geocopy); Base::Vector3d sp = geosymline->getStartPoint(); Base::Vector3d ep = geosymline->getEndPoint(); + Base::Vector3d ssp = geosymline->getStartPoint()+double(x)*displacement+double(y)*perpendicularDisplacement; - geosymline->setPoints( sp+double(x)*displacement+double(y)*perpendicularDisplacement, + geosymline->setPoints( ssp, ep+double(x)*displacement+double(y)*perpendicularDisplacement); + + if(it == geoIdList.begin()) + iterfirstpoint = ssp; } - else if(geosym->getTypeId() == Part::GeomCircle::getClassTypeId()){ - Part::GeomCircle *geosymcircle = static_cast(geosym); - Base::Vector3d cp = geosymcircle->getCenter(); + else if(geocopy->getTypeId() == Part::GeomCircle::getClassTypeId()){ + Part::GeomCircle *geosymcircle = static_cast(geocopy); + Base::Vector3d cp = geosymcircle->getCenter(); + Base::Vector3d scp = cp+double(x)*displacement+double(y)*perpendicularDisplacement; - geosymcircle->setCenter(cp+double(x)*displacement+double(y)*perpendicularDisplacement); + geosymcircle->setCenter(scp); + + if(it == geoIdList.begin()) + iterfirstpoint = scp; } - else if(geosym->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){ - Part::GeomArcOfCircle *geoaoc = static_cast(geosym); + else if(geocopy->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){ + Part::GeomArcOfCircle *geoaoc = static_cast(geocopy); Base::Vector3d cp = geoaoc->getCenter(); Base::Vector3d scp = cp+double(x)*displacement+double(y)*perpendicularDisplacement; geoaoc->setCenter(scp); + + if(it == geoIdList.begin()) + iterfirstpoint = geoaoc->getStartPoint(true); } - else if(geosym->getTypeId() == Part::GeomEllipse::getClassTypeId()){ - Part::GeomEllipse *geosymellipse = static_cast(geosym); + else if(geocopy->getTypeId() == Part::GeomEllipse::getClassTypeId()){ + Part::GeomEllipse *geosymellipse = static_cast(geocopy); Base::Vector3d cp = geosymellipse->getCenter(); Base::Vector3d scp = cp+double(x)*displacement+double(y)*perpendicularDisplacement; geosymellipse->setCenter(scp); + + if(it == geoIdList.begin()) + iterfirstpoint = scp; } - else if(geosym->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){ - Part::GeomArcOfEllipse *geosymaoe = static_cast(geosym); - Base::Vector3d cp = geosymaoe->getCenter(); + else if(geocopy->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){ + Part::GeomArcOfEllipse *geoaoe = static_cast(geocopy); + Base::Vector3d cp = geoaoe->getCenter(); Base::Vector3d scp = cp+double(x)*displacement+double(y)*perpendicularDisplacement; - geosymaoe->setCenter(scp); + geoaoe->setCenter(scp); + + if(it == geoIdList.begin()) + iterfirstpoint = geoaoe->getStartPoint(true); } - else if(geosym->getTypeId() == Part::GeomPoint::getClassTypeId()){ - Part::GeomPoint *geosympoint = static_cast(geosym); - Base::Vector3d cp = geosympoint->getPoint(); - - geosympoint->setPoint(cp+double(x)*displacement+double(y)*perpendicularDisplacement); + else if(geocopy->getTypeId() == Part::GeomPoint::getClassTypeId()){ + Part::GeomPoint *geopoint = static_cast(geocopy); + Base::Vector3d cp = geopoint->getPoint(); + Base::Vector3d scp = cp+double(x)*displacement+double(y)*perpendicularDisplacement; + geopoint->setPoint(scp); + + if(it == geoIdList.begin()) + iterfirstpoint = scp; } else { - Base::Console().Error("Unsupported Geometry!! Just copying it.\n"); + Base::Console().Error("Unsupported Geometry!! Just skipping it.\n"); + continue; } - newgeoVals.push_back(geosym); + newgeoVals.push_back(geocopy); geoIdMap.insert(std::make_pair(*it, cgeoid)); cgeoid++; } - // handle constraints + // handle geometry constraints 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*/) { - Constraint *constNew = (*it)->clone(); - constNew->First = geoIdMap[(*it)->First]; - newconstrVals.push_back(constNew); + if( (*it)->Second == Constraint::GeoUndef /*&& (*it)->Third == Constraint::GeoUndef*/) { + if( ((*it)->Type != Sketcher::DistanceX && (*it)->Type != Sketcher::DistanceY ) || + (*it)->FirstPos == Sketcher::none ) { // if it is not a point locking DistanceX/Y + Constraint *constNew = (*it)->clone(); + constNew->First = geoIdMap[(*it)->First]; + newconstrVals.push_back(constNew); + } } else { // other geoids intervene in this constraint @@ -2127,6 +2187,159 @@ int SketchObject::addCopy(const std::vector &geoIdList, const Base::Vector3 } } + // handle inter-geometry constraints + // example: App.ActiveDocument.Sketch.addArray([0,1], App.Vector(150,150,0),3,4,True) + if(constraindisplacement){ + + // add a construction line + Part::GeomLineSegment *constrline= new Part::GeomLineSegment(); + + Base::Vector3d spdisp = Vector3d(); // initializes to 0,0,0 + + Base::Vector3d sp = getPoint(refgeoid,refposId)+ ( ( x == 0 )? + (double(x)*displacement+double(y-1)*perpendicularDisplacement): + (double(x-1)*displacement+double(y)*perpendicularDisplacement)); // position of the reference point + Base::Vector3d ep = iterfirstpoint; // position of the current instance corresponding point + constrline->setPoints(sp,ep); + constrline->Construction=true; + + newgeoVals.push_back(constrline); + + Constraint *constNew; + + if(x == 0) { // first element of a row + + // add coincidents for construction line + constNew = new Constraint(); + constNew->Type = Sketcher::Coincident; + constNew->First = prevrowstartfirstgeoid; + constNew->FirstPos = refposId; + constNew->Second = cgeoid; + constNew->SecondPos = Sketcher::start; + newconstrVals.push_back(constNew); + + constNew = new Constraint(); + constNew->Type = Sketcher::Coincident; + constNew->First = iterfirstgeoid; + constNew->FirstPos = refposId; + constNew->Second = cgeoid; + constNew->SecondPos = Sketcher::end; + newconstrVals.push_back(constNew); + + if( y == 1 ) { // it is the first added element of this row in the perpendicular to displacementvector direction + rowrefgeoid = cgeoid; + cgeoid++; + + // add length (or equal if perpscale==1) and perpendicular + if(perpscale==1.0) { + constNew = new Constraint(); + constNew->Type = Sketcher::Equal; + constNew->First = rowrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->Second = colrefgeoid; + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + } else { + constNew = new Constraint(); + constNew->Type = Sketcher::Distance; + constNew->First = rowrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->Value = perpendicularDisplacement.Length(); + newconstrVals.push_back(constNew); + } + + constNew = new Constraint(); + constNew->Type = Sketcher::Perpendicular; + constNew->First = rowrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->Second = colrefgeoid; + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + } + else { // it is just one more element in the col direction + cgeoid++; + + // all other first rowers get an equality and perpendicular constraint + constNew = new Constraint(); + constNew->Type = Sketcher::Equal; + constNew->First = rowrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->Second = cgeoid-1; + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + + constNew = new Constraint(); + constNew->Type = Sketcher::Perpendicular; + constNew->First = cgeoid-1; + constNew->FirstPos = Sketcher::none; + constNew->Second = colrefgeoid; + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + + } + } + else { // any element not being the first element of a row + + // add coincidents for construction line + constNew = new Constraint(); + constNew->Type = Sketcher::Coincident; + constNew->First = prevfirstgeoid; + constNew->FirstPos = refposId; + constNew->Second = cgeoid; + constNew->SecondPos = Sketcher::start; + newconstrVals.push_back(constNew); + + constNew = new Constraint(); + constNew->Type = Sketcher::Coincident; + constNew->First = iterfirstgeoid; + constNew->FirstPos = refposId; + constNew->Second = cgeoid; + constNew->SecondPos = Sketcher::end; + newconstrVals.push_back(constNew); + + if(y == 0 && x == 1) { // first element of the first row + colrefgeoid = cgeoid; + cgeoid++; + + // add length and Angle + constNew = new Constraint(); + constNew->Type = Sketcher::Distance; + constNew->First = colrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->Value = displacement.Length(); + newconstrVals.push_back(constNew); + + constNew = new Constraint(); + constNew->Type = Sketcher::Angle; + constNew->First = colrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->Value = atan2(displacement.y,displacement.x); + newconstrVals.push_back(constNew); + + } + else { // any other element + cgeoid++; + + // all other elements get an equality and parallel constraint + constNew = new Constraint(); + constNew->Type = Sketcher::Equal; + constNew->First = colrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->Second = cgeoid-1; + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + + constNew = new Constraint(); + constNew->Type = Sketcher::Parallel; + constNew->First = cgeoid-1; + constNew->FirstPos = Sketcher::none; + constNew->Second = colrefgeoid; + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + } + } + } + geoIdMap.clear(); // after each creation reset map so that the key-value is univoque } } diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 8717d62049..2faf9b5942 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -152,8 +152,10 @@ public: 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); - /// adds a copy of the geometric elements displaced by the displacement vector - int addCopy(const std::vector &geoIdList, const Base::Vector3d& displacement, int csize=2, int rsize=1); + /// with default parameters adds a copy of the geometric elements displaced by the displacement vector. + /// It creates an array of csize elements in the direction of the displacement vector by rsize elements in the + /// direction perpendicular to the displacement vector, wherein the modulus of this perpendicular vector is scaled by perpscale. + int addCopy(const std::vector &geoIdList, const Base::Vector3d& displacement, int csize=2, int rsize=1, bool constraindisplacement = false, double perpscale = 1.0); /// Exposes all internal geometry of an object supporting internal geometry /*! * \return -1 on error diff --git a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp index 823d632b50..376b723913 100644 --- a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp +++ b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp @@ -834,8 +834,10 @@ PyObject* SketchObjectPy::addArray(PyObject *args) { PyObject *pcObj, *pcVect; int rows,cols; - - if (!PyArg_ParseTuple(args, "OO!ii", &pcObj, &(Base::VectorPy::Type), &pcVect,&rows,&cols)) + double perpscale=1.0; + PyObject* constraindisplacement= Py_False; + + if (!PyArg_ParseTuple(args, "OO!ii|O!d", &pcObj, &(Base::VectorPy::Type), &pcVect,&rows,&cols, &PyBool_Type, &constraindisplacement,&perpscale)) return 0; Base::Vector3d vect = static_cast(pcVect)->value(); @@ -849,7 +851,7 @@ PyObject* SketchObjectPy::addArray(PyObject *args) geoIdList.push_back(PyInt_AsLong((*it).ptr())); } - int ret = this->getSketchObjectPtr()->addCopy(geoIdList,vect,rows,cols) + 1; + int ret = this->getSketchObjectPtr()->addCopy(geoIdList,vect,rows,cols, PyObject_IsTrue(constraindisplacement) ? true : false, perpscale) + 1; if(ret == -1) throw Py::TypeError("Copy operation unsuccessful!"); diff --git a/src/Mod/Sketcher/Gui/CMakeLists.txt b/src/Mod/Sketcher/Gui/CMakeLists.txt index c472aaff69..7c8910a526 100644 --- a/src/Mod/Sketcher/Gui/CMakeLists.txt +++ b/src/Mod/Sketcher/Gui/CMakeLists.txt @@ -37,6 +37,7 @@ set(SketcherGui_MOC_HDRS TaskDlgEditSketch.h SketchOrientationDialog.h SketcherSettings.h + SketchLinearArrayDialog.h PropertyConstraintListItem.h ) fc_wrap_cpp(SketcherGui_MOC_SRCS ${SketcherGui_MOC_HDRS}) @@ -54,6 +55,7 @@ set(SketcherGui_UIC_SRCS InsertDatum.ui SketchOrientationDialog.ui SketcherSettings.ui + SketchLinearArrayDialog.ui ) qt4_wrap_ui(SketcherGui_UIC_HDRS ${SketcherGui_UIC_SRCS}) @@ -110,6 +112,8 @@ SET(SketcherGui_SRCS SketchOrientationDialog.h SketcherSettings.cpp SketcherSettings.h + SketchLinearArrayDialog.h + SketchLinearArrayDialog.cpp TaskDlgEditSketch.cpp TaskDlgEditSketch.h ViewProviderPython.cpp diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp index 8521448c60..db5935c897 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp @@ -37,10 +37,17 @@ #include #include +#include +#include + +#include "ViewProviderSketch.h" +#include "DrawSketchHandler.h" + #include #include #include "ViewProviderSketch.h" +#include "SketchLinearArrayDialog.h" using namespace std; using namespace SketcherGui; @@ -64,6 +71,19 @@ bool isSketcherAcceleratorActive(Gui::Document *doc, bool actsOnSelection ) return false; } +void ActivateAcceleratorHandler(Gui::Document *doc,DrawSketchHandler *handler) +{ + if (doc) { + if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom + (SketcherGui::ViewProviderSketch::getClassTypeId())) { + + SketcherGui::ViewProviderSketch* vp = dynamic_cast (doc->getInEdit()); + vp->purgeHandler(); + vp->activateHandler(handler); + } + } +} + // Close Shape Command DEF_STD_CMD_A(CmdSketcherCloseShape); @@ -1171,6 +1191,576 @@ bool CmdSketcherSymmetry::isActive(void) return isSketcherAcceleratorActive( getActiveGuiDocument(), true ); } +/* XPM */ +static const char *cursor_createcopy[]={ + "32 32 3 1", + "+ c white", + "# c red", + ". c None", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "......................###.......", + "...................####.#.......", + ".................###..###.......", + "...............###..............", + ".............###................", + "............###.................", + "...........##...................", + "............###.................", + ".............###................", + "...............###..............", + ".................###..###.......", + "...................####.#.......", + "......................###.......", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................"}; + + class DrawSketchHandlerCopy: public DrawSketchHandler + { + public: + DrawSketchHandlerCopy(string geoidlist, int origingeoid, Sketcher::PointPos originpos, int nelements): geoIdList(geoidlist), OriginGeoId (origingeoid), + OriginPos(originpos), nElements(nelements), Mode(STATUS_SEEK_First), EditCurve(2){} + virtual ~DrawSketchHandlerCopy(){} + /// mode table + enum SelectMode { + STATUS_SEEK_First, /**< enum value ----. */ + STATUS_End + }; + + virtual void activated(ViewProviderSketch *sketchgui) + { + setCursor(QPixmap(cursor_createcopy),7,7); + Origin = static_cast(sketchgui->getObject())->getPoint(OriginGeoId, OriginPos); + EditCurve[0] = Base::Vector2D(Origin.x,Origin.y); + } + + virtual void mouseMove(Base::Vector2D onSketchPos) + { + if (Mode==STATUS_SEEK_First) { + float length = (onSketchPos - EditCurve[0]).Length(); + float angle = (onSketchPos - EditCurve[0]).GetAngle(Base::Vector2D(1.f,0.f)); + SbString text; + text.sprintf(" (%.1f,%.1fdeg)", length, angle * 180 / M_PI); + setPositionText(onSketchPos, text); + + EditCurve[1] = onSketchPos; + sketchgui->drawEdit(EditCurve); + if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.0,0.0),AutoConstraint::VERTEX)) { + renderSuggestConstraintsCursor(sugConstr1); + return; + } + + } + applyCursor(); + } + + virtual bool pressButton(Base::Vector2D onSketchPos) + { + if (Mode==STATUS_SEEK_First){ + EditCurve[1] = onSketchPos; + sketchgui->drawEdit(EditCurve); + Mode = STATUS_End; + } + + return true; + } + + virtual bool releaseButton(Base::Vector2D onSketchPos) + { + if (Mode==STATUS_End){ + + Base::Vector2D vector = EditCurve[1]-EditCurve[0]; + + unsetCursor(); + resetPositionText(); + + Gui::Command::openCommand("Create copy of 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.addCopy(%s,App.Vector(%f,%f,0))", + sketchgui->getObject()->getNameInDocument(), + geoIdList.c_str(), vector.fX, vector.fY + ); + + Gui::Command::commitCommand(); + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\n", e.what()); + Gui::Command::abortCommand(); + } + + // add auto constraints for the destination copy + if (sugConstr1.size() > 0) { + createAutoConstraints(sugConstr1, OriginGeoId+nElements, OriginPos); + sugConstr1.clear(); + } + + if(autoRecompute) + Gui::Command::updateActive(); + else + static_cast(sketchgui->getObject())->solve(); + + EditCurve.clear(); + sketchgui->drawEdit(EditCurve); + + sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider + } + return true; + } + protected: + SelectMode Mode; + string geoIdList; + Base::Vector3d Origin; + int OriginGeoId; + Sketcher::PointPos OriginPos; + int nElements; + std::vector EditCurve; + std::vector sugConstr1; + }; + +DEF_STD_CMD_A(CmdSketcherCopy); + +CmdSketcherCopy::CmdSketcherCopy() +:Command("Sketcher_Copy") +{ + sAppModule = "Sketcher"; + sGroup = QT_TR_NOOP("Sketcher"); + sMenuText = QT_TR_NOOP("Copy"); + sToolTipText = QT_TR_NOOP("Creates a copy of the geometry taking as reference the last selected point"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Sketcher_Copy"; + sAccel = "CTRL+C"; + eType = ForEdit; +} + +void CmdSketcherCopy::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; + + // 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" ) { + LastGeoId = std::atoi(it->substr(4,4000).c_str()) - 1; + LastPointPos = Sketcher::none; + + LastGeo = Obj->getGeometry(LastGeoId); + + // lines to copy + 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; + // points to copy + if(LastGeoId>=0) { + geoids++; + stream << LastGeoId << ","; + } + } + } + } + + // 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; + } + } + + if ( geoids < 1 ) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("A copy requires at least one selected non-external geometric element")); + return; + } + + std::string geoIdList = stream.str(); + + // remove the last added comma and brackets to make the python list + int index = geoIdList.rfind(','); + geoIdList.resize(index); + geoIdList.insert(0,1,'['); + geoIdList.append(1,']'); + + // if the last element is not a point serving as a reference for the copy process + // then make the start point of the last element the copy reference (if it exists, if not the center point) + if(LastPointPos == Sketcher::none){ + if( LastGeo->getTypeId() == Part::GeomCircle::getClassTypeId() || + LastGeo->getTypeId() == Part::GeomEllipse::getClassTypeId() ) { + LastPointPos = Sketcher::mid; + } + else { + LastPointPos = Sketcher::start; + } + } + + ActivateAcceleratorHandler(getActiveGuiDocument(),new DrawSketchHandlerCopy(geoIdList, LastGeoId, LastPointPos, geoids)); +} + + + +bool CmdSketcherCopy::isActive(void) +{ + return isSketcherAcceleratorActive( getActiveGuiDocument(), true ); +} + +/* XPM */ +static const char *cursor_createlineararray[]={ + "32 32 3 1", + "+ c white", + "# c red", + ". c None", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + ".....###.............###........", + "....#.####.........####.#.......", + "....###.###......###..###.......", + ".........###...###..............", + "...........#####................", + "............###.................", + "...........#####................", + "............###.................", + "...........#####................", + "........###....###..............", + ".###..###........###..###.......", + ".#.####............####.#.......", + ".###..................###.......", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................"}; + + class DrawSketchHandlerLinearArray: public DrawSketchHandler + { + public: + DrawSketchHandlerLinearArray(string geoidlist, int origingeoid, Sketcher::PointPos originpos, int nelements, + int rows,int cols,bool constraintSeparation, + bool equalVerticalHorizontalSpacing ): geoIdList(geoidlist), OriginGeoId (origingeoid), + Rows(rows), Cols(cols), ConstraintSeparation(constraintSeparation), EqualVerticalHorizontalSpacing(equalVerticalHorizontalSpacing), + OriginPos(originpos), nElements(nelements), Mode(STATUS_SEEK_First), EditCurve(2){} + virtual ~DrawSketchHandlerLinearArray(){} + /// mode table + enum SelectMode { + STATUS_SEEK_First, /**< enum value ----. */ + STATUS_End + }; + + virtual void activated(ViewProviderSketch *sketchgui) + { + setCursor(QPixmap(cursor_createlineararray),7,7); + Origin = static_cast(sketchgui->getObject())->getPoint(OriginGeoId, OriginPos); + EditCurve[0] = Base::Vector2D(Origin.x,Origin.y); + } + + virtual void mouseMove(Base::Vector2D onSketchPos) + { + if (Mode==STATUS_SEEK_First) { + float length = (onSketchPos - EditCurve[0]).Length(); + float angle = (onSketchPos - EditCurve[0]).GetAngle(Base::Vector2D(1.f,0.f)); + SbString text; + text.sprintf(" (%.1f,%.1fdeg)", length, angle * 180 / M_PI); + setPositionText(onSketchPos, text); + + EditCurve[1] = onSketchPos; + sketchgui->drawEdit(EditCurve); + if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.0,0.0),AutoConstraint::VERTEX)) { + renderSuggestConstraintsCursor(sugConstr1); + return; + } + + } + applyCursor(); + } + + virtual bool pressButton(Base::Vector2D onSketchPos) + { + if (Mode==STATUS_SEEK_First){ + EditCurve[1] = onSketchPos; + sketchgui->drawEdit(EditCurve); + Mode = STATUS_End; + } + + return true; + } + + virtual bool releaseButton(Base::Vector2D onSketchPos) + { + if (Mode==STATUS_End){ + + Base::Vector2D vector = EditCurve[1]-EditCurve[0]; + + unsetCursor(); + resetPositionText(); + + Gui::Command::openCommand("Create copy of 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.addArray(%s, App.Vector(%f,%f,0),%d,%d,%s,%f)", + sketchgui->getObject()->getNameInDocument(), + geoIdList.c_str(), vector.fX, vector.fY, + Cols, Rows, + (ConstraintSeparation?"True":"False"), + (EqualVerticalHorizontalSpacing?1.0:0.5)); + + Gui::Command::commitCommand(); + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\n", e.what()); + Gui::Command::abortCommand(); + } + + // add auto constraints for the destination copy + if (sugConstr1.size() > 0) { + createAutoConstraints(sugConstr1, OriginGeoId+nElements, OriginPos); + sugConstr1.clear(); + } + + if(autoRecompute) + Gui::Command::updateActive(); + else + static_cast(sketchgui->getObject())->solve(); + + EditCurve.clear(); + sketchgui->drawEdit(EditCurve); + + sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider + } + return true; + } + protected: + SelectMode Mode; + string geoIdList; + Base::Vector3d Origin; + int OriginGeoId; + Sketcher::PointPos OriginPos; + int nElements; + int Rows; + int Cols; + bool ConstraintSeparation; + bool EqualVerticalHorizontalSpacing; + std::vector EditCurve; + std::vector sugConstr1; + }; + + +DEF_STD_CMD_A(CmdSketcherLinearArray); + +CmdSketcherLinearArray::CmdSketcherLinearArray() +:Command("Sketcher_LinearArray") +{ + sAppModule = "Sketcher"; + sGroup = QT_TR_NOOP("Sketcher"); + sMenuText = QT_TR_NOOP("LinearArray"); + sToolTipText = QT_TR_NOOP("Creates a lineararray of the geometry taking as reference the last selected point"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Sketcher_LinearArray"; + sAccel = ""; + eType = ForEdit; +} + +void CmdSketcherLinearArray::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; + + // 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" ) { + LastGeoId = std::atoi(it->substr(4,4000).c_str()) - 1; + LastPointPos = Sketcher::none; + + LastGeo = Obj->getGeometry(LastGeoId); + + // lines to copy + 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; + // points to copy + if(LastGeoId>=0) { + geoids++; + stream << LastGeoId << ","; + } + } + } + } + + // 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; + } + } + + if ( geoids < 1 ) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("A copy requires at least one selected non-external geometric element")); + return; + } + + std::string geoIdList = stream.str(); + + // remove the last added comma and brackets to make the python list + int index = geoIdList.rfind(','); + geoIdList.resize(index); + geoIdList.insert(0,1,'['); + geoIdList.append(1,']'); + + // if the last element is not a point serving as a reference for the copy process + // then make the start point of the last element the copy reference (if it exists, if not the center point) + if(LastPointPos == Sketcher::none){ + if( LastGeo->getTypeId() == Part::GeomCircle::getClassTypeId() || + LastGeo->getTypeId() == Part::GeomEllipse::getClassTypeId() ) { + LastPointPos = Sketcher::mid; + } + else { + LastPointPos = Sketcher::start; + } + } + + // Pop-up asking for values + SketchLinearArrayDialog * slad = new SketchLinearArrayDialog(); + + if( slad->exec() == QDialog::Accepted ) + ActivateAcceleratorHandler(getActiveGuiDocument(), + new DrawSketchHandlerLinearArray(geoIdList, LastGeoId, LastPointPos, geoids, + slad->Rows, slad->Cols, slad->ConstraintSeparation, + slad->EqualVerticalHorizontalSpacing)); + + delete slad; + +} + + + +bool CmdSketcherLinearArray::isActive(void) +{ + return isSketcherAcceleratorActive( getActiveGuiDocument(), true ); +} + void CreateSketcherCommandsConstraintAccel(void) { Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager(); @@ -1185,5 +1775,7 @@ void CreateSketcherCommandsConstraintAccel(void) rcCmdMgr.addCommand(new CmdSketcherSelectConflictingConstraints()); rcCmdMgr.addCommand(new CmdSketcherSelectElementsAssociatedWithConstraints()); rcCmdMgr.addCommand(new CmdSketcherRestoreInternalAlignmentGeometry()); - rcCmdMgr.addCommand(new CmdSketcherSymmetry()); + rcCmdMgr.addCommand(new CmdSketcherSymmetry()); + rcCmdMgr.addCommand(new CmdSketcherCopy()); + rcCmdMgr.addCommand(new CmdSketcherLinearArray()); } diff --git a/src/Mod/Sketcher/Gui/SketchLinearArrayDialog.cpp b/src/Mod/Sketcher/Gui/SketchLinearArrayDialog.cpp new file mode 100644 index 0000000000..3524573b48 --- /dev/null +++ b/src/Mod/Sketcher/Gui/SketchLinearArrayDialog.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (c) 2015 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +#endif + +#include +#include +#include +#include + +#include "ui_SketchLinearArrayDialog.h" +#include "SketchLinearArrayDialog.h" + +using namespace SketcherGui; + +SketchLinearArrayDialog::SketchLinearArrayDialog(void) + : QDialog(Gui::getMainWindow()), ui(new Ui_SketchLinearArrayDialog) +{ + ui->setupUi(this); + + ui->RowsQuantitySpinBox->onRestore(); + ui->ColsQuantitySpinBox->onRestore(); + ui->ConstraintSeparationCheckBox->onRestore(); + ui->EqualVerticalHorizontalSpacingCheckBox->onRestore(); + + updateValues(); +} + +SketchLinearArrayDialog::~SketchLinearArrayDialog() +{ + +} + +void SketchLinearArrayDialog::accept() +{ + ui->RowsQuantitySpinBox->onSave(); + ui->ColsQuantitySpinBox->onSave(); + ui->ConstraintSeparationCheckBox->onSave(); + ui->EqualVerticalHorizontalSpacingCheckBox->onSave(); + + updateValues(); + + QDialog::accept(); +} + +void SketchLinearArrayDialog::updateValues(void) +{ + Rows = ui->RowsQuantitySpinBox->value(); + Cols = ui->ColsQuantitySpinBox->value(); + ConstraintSeparation = ui->ConstraintSeparationCheckBox->isChecked(); + EqualVerticalHorizontalSpacing = ui->EqualVerticalHorizontalSpacingCheckBox->isChecked(); +} + +#include "moc_SketchLinearArrayDialog.cpp" diff --git a/src/Mod/Sketcher/Gui/SketchLinearArrayDialog.h b/src/Mod/Sketcher/Gui/SketchLinearArrayDialog.h new file mode 100644 index 0000000000..ca7642ceac --- /dev/null +++ b/src/Mod/Sketcher/Gui/SketchLinearArrayDialog.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (c) 2015 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef SKETCHERGUI_SketchLinearArrayDialog_H +#define SKETCHERGUI_SketchLinearArrayDialog_H + +#include +#include + +namespace SketcherGui { + +class Ui_SketchLinearArrayDialog; +class SketchLinearArrayDialog : public QDialog +{ + Q_OBJECT + +public: + SketchLinearArrayDialog(void); + ~SketchLinearArrayDialog(); + + void accept(); + + int Rows; + int Cols; + bool ConstraintSeparation; + bool EqualVerticalHorizontalSpacing; + +protected: + void updateValues(void); +private: + Ui_SketchLinearArrayDialog* ui; +}; + +} + +#endif // SKETCHERGUI_SketchLinearArrayDialog_H diff --git a/src/Mod/Sketcher/Gui/SketchLinearArrayDialog.ui b/src/Mod/Sketcher/Gui/SketchLinearArrayDialog.ui new file mode 100644 index 0000000000..68e58213f0 --- /dev/null +++ b/src/Mod/Sketcher/Gui/SketchLinearArrayDialog.ui @@ -0,0 +1,187 @@ + + + SketcherGui::SketchLinearArrayDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 287 + 177 + + + + Create array + + + + + + + + Columns: + + + + + + + Number of columns of the linear array + + + 2 + + + DefaultArrayColumnNumber + + + Mod/Sketcher + + + + + + + + + + + Rows: + + + + + + + Number of rows of the linear array + + + 1 + + + DefaultArrayRowNumber + + + Mod/Sketcher + + + + + + + + + Makes the inter-row and inter-col spacing the same if clicked + + + Equal vertical/horizontal spacing + + + DefaultEqualVerticalHorizontalSpacing + + + Mod/Sketcher + + + + + + + if selected, each element in the array is constraint with respect to the others using construction lines + + + Qt::LeftToRight + + + Constrain inter-element separation + + + true + + + DefaultConstraintArrayElements + + + Mod/Sketcher + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + true + + + + + + + + Gui::PrefSpinBox + QSpinBox +
Gui/PrefWidgets.h
+
+ + Gui::PrefCheckBox + QCheckBox +
Gui/PrefWidgets.h
+
+
+ + + + buttonBox + accepted() + SketcherGui::SketchLinearArrayDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SketcherGui::SketchLinearArrayDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/src/Mod/Sketcher/Gui/Workbench.cpp b/src/Mod/Sketcher/Gui/Workbench.cpp index e359902d5c..9074dbfd8a 100644 --- a/src/Mod/Sketcher/Gui/Workbench.cpp +++ b/src/Mod/Sketcher/Gui/Workbench.cpp @@ -246,7 +246,10 @@ inline void SketcherAddWorkbenchTools(Gui::MenuItem& consaccel){ << "Sketcher_SelectConflictingConstraints" << "Sketcher_SelectElementsAssociatedWithConstraints" << "Sketcher_RestoreInternalAlignmentGeometry" - << "Sketcher_Symmetry"; + << "Sketcher_Symmetry" + << "Sketcher_Copy" + << "Sketcher_LinearArray"; + } template <> inline void SketcherAddWorkbenchTools(Gui::ToolBarItem& consaccel){ @@ -254,7 +257,9 @@ inline void SketcherAddWorkbenchTools(Gui::ToolBarItem& consac << "Sketcher_ConnectLines" << "Sketcher_SelectConstraints" << "Sketcher_RestoreInternalAlignmentGeometry" - << "Sketcher_Symmetry"; + << "Sketcher_Symmetry" + << "Sketcher_Copy" + << "Sketcher_LinearArray"; } template