diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 28579d8c5..429b0df82 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 8717d6204..2faf9b594 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 823d632b5..376b72391 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 c472aaff6..7c8910a52 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 8521448c6..db5935c89 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 000000000..3524573b4 --- /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 000000000..ca7642cea --- /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 000000000..68e58213f --- /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 e359902d5..9074dbfd8 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