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
This commit is contained in:
Abdullah Tahiri 2015-08-05 14:45:27 +02:00 committed by wmayer
parent ee43612125
commit 6a16910ba5
9 changed files with 1178 additions and 40 deletions

View File

@ -2011,7 +2011,8 @@ int SketchObject::addSymmetric(const std::vector<int> &geoIdList, int refGeoId,
return Geometry.getSize()-1;
}
int SketchObject::addCopy(const std::vector<int> &geoIdList, const Base::Vector3d& displacement, int csize/*=2*/, int rsize/*=1*/)
int SketchObject::addCopy(const std::vector<int> &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<int> &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<int, int> 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;y<rsize;y++) {
for (y=0;y<rsize;y++) {
for (x=0;x<csize;x++) {
if(x == 0 && y == 0)
if(x == 0 && y == 0) { // the reference for constraining array elements is the first valid point of the first element
const Part::Geometry *geo = getGeometry(*(geoIdList.begin()));
refgeoid=*(geoIdList.begin());
currentrowfirstgeoid = refgeoid;
iterfirstgeoid = refgeoid;
if(geo->getTypeId() == 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<int>::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<Part::GeomLineSegment *>(geosym);
if(geocopy->getTypeId() == Part::GeomLineSegment::getClassTypeId()){
Part::GeomLineSegment *geosymline = static_cast<Part::GeomLineSegment *>(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<Part::GeomCircle *>(geosym);
Base::Vector3d cp = geosymcircle->getCenter();
else if(geocopy->getTypeId() == Part::GeomCircle::getClassTypeId()){
Part::GeomCircle *geosymcircle = static_cast<Part::GeomCircle *>(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<Part::GeomArcOfCircle *>(geosym);
else if(geocopy->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){
Part::GeomArcOfCircle *geoaoc = static_cast<Part::GeomArcOfCircle *>(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<Part::GeomEllipse *>(geosym);
else if(geocopy->getTypeId() == Part::GeomEllipse::getClassTypeId()){
Part::GeomEllipse *geosymellipse = static_cast<Part::GeomEllipse *>(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<Part::GeomArcOfEllipse *>(geosym);
Base::Vector3d cp = geosymaoe->getCenter();
else if(geocopy->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){
Part::GeomArcOfEllipse *geoaoe = static_cast<Part::GeomArcOfEllipse *>(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<Part::GeomPoint *>(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<Part::GeomPoint *>(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<Constraint *>::const_iterator it = constrvals.begin(); it != constrvals.end(); ++it) {
std::vector<int>::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<int> &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
}
}

View File

@ -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<int> &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<int> &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<int> &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

View File

@ -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<Base::VectorPy*>(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!");

View File

@ -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

View File

@ -37,10 +37,17 @@
#include <Gui/MainWindow.h>
#include <Gui/DlgEditFileIncludeProptertyExternal.h>
#include <Gui/Action.h>
#include <Gui/BitmapFactory.h>
#include "ViewProviderSketch.h"
#include "DrawSketchHandler.h"
#include <Mod/Part/App/Geometry.h>
#include <Mod/Sketcher/App/SketchObject.h>
#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<SketcherGui::ViewProviderSketch*> (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<Sketcher::SketchObject *>(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<Sketcher::SketchObject *>(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<Base::Vector2D> EditCurve;
std::vector<AutoConstraint> 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<Gui::SelectionObject> selection = getSelection().getSelectionEx();
Sketcher::SketchObject* Obj = dynamic_cast<Sketcher::SketchObject*>(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<std::string> &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<std::string>::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<Sketcher::SketchObject *>(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<Sketcher::SketchObject *>(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<Base::Vector2D> EditCurve;
std::vector<AutoConstraint> 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<Gui::SelectionObject> selection = getSelection().getSelectionEx();
Sketcher::SketchObject* Obj = dynamic_cast<Sketcher::SketchObject*>(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<std::string> &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<std::string>::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());
}

View File

@ -0,0 +1,78 @@
/***************************************************************************
* Copyright (c) 2015 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
* *
* 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 <QPixmap>
# include <QDialog>
#endif
#include <Gui/BitmapFactory.h>
#include <Gui/MainWindow.h>
#include <Base/Tools.h>
#include <Base/UnitsApi.h>
#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"

View File

@ -0,0 +1,55 @@
/***************************************************************************
* Copyright (c) 2015 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
* *
* 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 <Base/Placement.h>
#include <QDialog>
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

View File

@ -0,0 +1,187 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SketcherGui::SketchLinearArrayDialog</class>
<widget class="QDialog" name="SketcherGui::SketchLinearArrayDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>287</width>
<height>177</height>
</rect>
</property>
<property name="windowTitle">
<string>Create array</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Columns:</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefSpinBox" name="ColsQuantitySpinBox">
<property name="toolTip">
<string>Number of columns of the linear array</string>
</property>
<property name="minimum">
<number>2</number>
</property>
<property name="prefEntry" stdset="0">
<cstring>DefaultArrayColumnNumber</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Sketcher</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Rows:</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefSpinBox" name="RowsQuantitySpinBox">
<property name="toolTip">
<string>Number of rows of the linear array</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="prefEntry" stdset="0">
<cstring>DefaultArrayRowNumber</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Sketcher</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="EqualVerticalHorizontalSpacingCheckBox">
<property name="toolTip">
<string>Makes the inter-row and inter-col spacing the same if clicked</string>
</property>
<property name="text">
<string>Equal vertical/horizontal spacing</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>DefaultEqualVerticalHorizontalSpacing</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Sketcher</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="ConstraintSeparationCheckBox">
<property name="toolTip">
<string>if selected, each element in the array is constraint with respect to the others using construction lines</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Constrain inter-element separation</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>DefaultConstraintArrayElements</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Sketcher</cstring>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
<property name="centerButtons">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::PrefSpinBox</class>
<extends>QSpinBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefCheckBox</class>
<extends>QCheckBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>SketcherGui::SketchLinearArrayDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>SketcherGui::SketchLinearArrayDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -246,7 +246,10 @@ inline void SketcherAddWorkbenchTools<Gui::MenuItem>(Gui::MenuItem& consaccel){
<< "Sketcher_SelectConflictingConstraints"
<< "Sketcher_SelectElementsAssociatedWithConstraints"
<< "Sketcher_RestoreInternalAlignmentGeometry"
<< "Sketcher_Symmetry";
<< "Sketcher_Symmetry"
<< "Sketcher_Copy"
<< "Sketcher_LinearArray";
}
template <>
inline void SketcherAddWorkbenchTools<Gui::ToolBarItem>(Gui::ToolBarItem& consaccel){
@ -254,7 +257,9 @@ inline void SketcherAddWorkbenchTools<Gui::ToolBarItem>(Gui::ToolBarItem& consac
<< "Sketcher_ConnectLines"
<< "Sketcher_SelectConstraints"
<< "Sketcher_RestoreInternalAlignmentGeometry"
<< "Sketcher_Symmetry";
<< "Sketcher_Symmetry"
<< "Sketcher_Copy"
<< "Sketcher_LinearArray";
}
template <typename T>