diff --git a/src/Mod/Sketcher/App/ConstraintPyImp.cpp b/src/Mod/Sketcher/App/ConstraintPyImp.cpp index a7d980956..fb944a153 100644 --- a/src/Mod/Sketcher/App/ConstraintPyImp.cpp +++ b/src/Mod/Sketcher/App/ConstraintPyImp.cpp @@ -290,6 +290,28 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/) this->getConstraintPtr()->ThirdPos = (Sketcher::PointPos) intArg4; return 0; } + else if (strstr(ConstraintType,"InternalAlignment") != NULL) { // InteralAlignment with InternalElementIndex argument + this->getConstraintPtr()->Type = InternalAlignment; + + valid = true; + + if(strstr(ConstraintType,"BSplineControlPoint") != NULL) { + this->getConstraintPtr()->AlignmentType=BSplineControlPoint; + } + else { + this->getConstraintPtr()->AlignmentType=Undef; + valid = false; + } + + if (valid) { + this->getConstraintPtr()->First = intArg1; + this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) intArg2; + this->getConstraintPtr()->Second = intArg3; + this->getConstraintPtr()->InternalAlignmentIndex = intArg4; + return 0; + } + + } if (valid) { this->getConstraintPtr()->First = intArg1; this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) intArg2; diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp index f9a441449..32b306c65 100644 --- a/src/Mod/Sketcher/App/Sketch.cpp +++ b/src/Mod/Sketcher/App/Sketch.cpp @@ -671,6 +671,32 @@ int Sketch::addBSpline(const Part::GeomBSplineCurve &bspline, bool fixed) std::vector mult = bsp->getMultiplicities(); int degree = bsp->getDegree(); bool periodic = bsp->isPeriodic(); + + // OCC hack + // c means there is a constraint on that weight, nc no constraint + // OCC provides normalized weights when polynomic [1 1 1] [c c c] and unnormalized weights when rational [5 1 5] [c nc c] + // then when changing from polynomic to rational, after the first solve any not-constrained pole circle gets normalized to 1. + // This only happens when changing from polynomic to rational, any subsequent change remains unnormalized [5 1 5] [c nc nc] + // This creates a visual problem that one of the poles shrinks to 1 mm when deleting an equality constraint. + + int lastoneindex = -1; + int countones = 0; + double lastnotone = 1.0; + + for(size_t i = 0; i < weights.size(); i++) { + if(weights[i] != 1.0) { + lastnotone = weights[i]; + } + else { // is 1.0 + lastoneindex = i; + countones++; + } + } + + if (countones == 1) + weights[lastoneindex] = (lastnotone * 0.99); + + // end hack Base::Vector3d startPnt = bsp->getStartPoint(); Base::Vector3d endPnt = bsp->getEndPoint(); diff --git a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp index 1a24e4091..6dafdcbb9 100644 --- a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp +++ b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp @@ -4326,6 +4326,8 @@ public: , EditCurve(2) , CurrentConstraint(0) , ConstrMethod(constructionMethod) + , IsClosed(false) + , FirstPoleGeoId(-2000) { std::vector sugConstr1; sugConstr.push_back(sugConstr1); @@ -4382,20 +4384,109 @@ public: EditCurve[0] = onSketchPos; Mode = STATUS_SEEK_ADDITIONAL_CONTROLPOINTS; + + // insert circle point for pole, defer internal alignment constraining. + try { + Gui::Command::openCommand("Add Pole circle"); + + //Add pole + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),10),True)", + sketchgui->getObject()->getNameInDocument(), + EditCurve[0].x,EditCurve[0].y); + + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\n", e.what()); + Gui::Command::abortCommand(); + + static_cast(sketchgui->getObject())->solve(); + + return false; + } + + //Gui::Command::commitCommand(); + + //static_cast(sketchgui->getObject())->solve(); + + FirstPoleGeoId = getHighestCurveIndex(); + + // add auto constraints on pole + if (sugConstr[CurrentConstraint].size() > 0) { + createAutoConstraints(sugConstr[CurrentConstraint], FirstPoleGeoId, Sketcher::mid, false); + } + + static_cast(sketchgui->getObject())->solve(); + std::vector sugConstrN; sugConstr.push_back(sugConstrN); CurrentConstraint++; + } else if (Mode == STATUS_SEEK_ADDITIONAL_CONTROLPOINTS) { EditCurve[EditCurve.size()-1] = onSketchPos; - // finish adding controlpoints on double click - if (EditCurve[EditCurve.size()-2] == EditCurve[EditCurve.size()-1]) { - EditCurve.pop_back(); - Mode = STATUS_CLOSE; + // check if coincident with first pole + for(std::vector::const_iterator it = sugConstr[CurrentConstraint].begin(); it != sugConstr[CurrentConstraint].end(); it++) { + if( (*it).Type == Sketcher::Coincident && (*it).GeoId == FirstPoleGeoId && (*it).PosId == Sketcher::mid ) { + + IsClosed = true; + } } - else { + + if (IsClosed) { + Mode = STATUS_CLOSE; + + if (ConstrMethod == 1) { // if periodic we do not need the last pole + EditCurve.pop_back(); + sugConstr.pop_back(); + + return true; + } + + + } + + // insert circle point for pole, defer internal alignment constraining. + try { + + //Gui::Command::openCommand("Add Pole circle"); + + //Add pole + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),10),True)", + sketchgui->getObject()->getNameInDocument(), + EditCurve[EditCurve.size()-1].x,EditCurve[EditCurve.size()-1].y); + + if(EditCurve.size() == 2) { + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Radius',%d,%f)) ", + sketchgui->getObject()->getNameInDocument(), FirstPoleGeoId, round( (EditCurve[1]-EditCurve[0]).Length()/6 )); + } + + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Equal',%d,%d)) ", + sketchgui->getObject()->getNameInDocument(), FirstPoleGeoId, FirstPoleGeoId+ EditCurve.size()-1); + + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\n", e.what()); + Gui::Command::abortCommand(); + + static_cast(sketchgui->getObject())->solve(); + + return false; + } + + //Gui::Command::commitCommand(); + + //static_cast(sketchgui->getObject())->solve(); + + // add auto constraints on pole + if (sugConstr[CurrentConstraint].size() > 0) { + createAutoConstraints(sugConstr[CurrentConstraint], FirstPoleGeoId + EditCurve.size()-1, Sketcher::mid, false); + } + + //static_cast(sketchgui->getObject())->solve(); + + if (!IsClosed) { EditCurve.resize(EditCurve.size() + 1); // add one place for a pole std::vector sugConstrN; sugConstr.push_back(sugConstrN); @@ -4432,25 +4523,34 @@ public: try { - Gui::Command::openCommand("Add B-spline curve"); + //Gui::Command::openCommand("Add B-spline curve"); - //Add arc of parabola - Gui::Command::doCommand(Gui::Command::Doc, - "App.ActiveDocument.%s.addGeometry(Part.BSplineCurve" - "(%s,%s)," - "%s)", - sketchgui->getObject()->getNameInDocument(), - controlpoints.c_str(), - ConstrMethod == 0 ?"False":"True", - geometryCreationMode==Construction?"True":"False"); + //Add arc of parabola + Gui::Command::doCommand(Gui::Command::Doc, + "App.ActiveDocument.%s.addGeometry(Part.BSplineCurve" + "(%s,%s)," + "%s)", + sketchgui->getObject()->getNameInDocument(), + controlpoints.c_str(), + ConstrMethod == 0 ?"False":"True", + geometryCreationMode==Construction?"True":"False"); - currentgeoid++; - - Gui::Command::doCommand(Gui::Command::Doc, - "App.ActiveDocument.%s.ExposeInternalGeometry(%d)", - sketchgui->getObject()->getNameInDocument(), - currentgeoid); + currentgeoid++; + // Constraint pole circles to bspline. + std::stringstream cstream; + + cstream << "conList = []\n"; + + for (size_t i = 0; i < EditCurve.size(); i++) { + cstream << "conList.append(Sketcher.Constraint('InternalAlignment:Sketcher::BSplineControlPoint'," << FirstPoleGeoId+i + << "," << Sketcher::mid << "," << currentgeoid << "," << i << "))\n"; + } + + cstream << "App.ActiveDocument."<< sketchgui->getObject()->getNameInDocument() << ".addConstraint(conList)\n"; + + Gui::Command::doCommand(Gui::Command::Doc, cstream.str().c_str()); + } catch (const Base::Exception& e) { Base::Console().Error("%s\n", e.what()); @@ -4469,15 +4569,6 @@ public: Gui::Command::commitCommand(); - int poleindex=0; - for(std::vector>::iterator it=sugConstr.begin(); it != sugConstr.end(); it++, poleindex++) { - // add auto constraints - if ((*it).size() > 0) { - createAutoConstraints((*it), currentgeoid+1+poleindex, Sketcher::mid); - (*it).clear(); - } - } - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); bool autoRecompute = hGrp->GetBool("AutoRecompute",false); @@ -4495,6 +4586,15 @@ public: sketchgui->drawEdit(EditCurve); EditCurve.resize(2); applyCursor(); + + sugConstr.clear(); + + std::vector sugConstr1; + sugConstr.push_back(sugConstr1); + + CurrentConstraint=0; + IsClosed=false; + /* It is ok not to call to purgeHandler * in continuous creation mode because the * handler is destroyed by the quit() method on pressing the @@ -4506,6 +4606,60 @@ public: } return true; } + + virtual void quit(void) { + // We must see if we need to create a BSpline before cancelling everything + // and now just like any other Handler, + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); + + bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true); + + if (CurrentConstraint > 1) { + // create bspline from existing poles + Mode=STATUS_CLOSE; + EditCurve.pop_back(); + this->releaseButton(Base::Vector2d(0.f,0.f)); + } + else if(CurrentConstraint == 1) { + // if we just have one point and we can not close anything, then cancel this creation but continue according to continuous mode + //sketchgui->getDocument()->undo(1); + + Gui::Command::abortCommand(); + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); + bool autoRecompute = hGrp->GetBool("AutoRecompute",false); + + if(autoRecompute) + Gui::Command::updateActive(); + else + static_cast(sketchgui->getObject())->solve(); + + if(!continuousMode){ + DrawSketchHandler::quit(); + } + else { + // This code disregards existing data and enables the continuous creation mode. + Mode = STATUS_SEEK_FIRST_CONTROLPOINT; + EditCurve.clear(); + sketchgui->drawEdit(EditCurve); + EditCurve.resize(2); + applyCursor(); + + sugConstr.clear(); + + std::vector sugConstr1; + sugConstr.push_back(sugConstr1); + + CurrentConstraint=0; + IsClosed=false; + } + } + else { // we have no data (CurrentConstraint == 0) so user when right-clicking really wants to exit + DrawSketchHandler::quit(); + } + } + protected: SELECT_MODE Mode; @@ -4515,6 +4669,8 @@ protected: int CurrentConstraint; int ConstrMethod; + bool IsClosed; + int FirstPoleGeoId; }; DEF_STD_CMD_A(CmdSketcherCreateBSpline) diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp index fb4554e7a..cc8a783ec 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp @@ -383,15 +383,18 @@ int DrawSketchHandler::seekAutoConstraint(std::vector &suggested } void DrawSketchHandler::createAutoConstraints(const std::vector &autoConstrs, - int geoId1, Sketcher::PointPos posId1) + int geoId1, Sketcher::PointPos posId1, bool createowncommand /*= true*/) { if (!sketchgui->Autoconstraints.getValue()) return; // If Autoconstraints property is not set quit if (autoConstrs.size() > 0) { - // Open the Command - Gui::Command::openCommand("Add auto constraints"); - + + if(createowncommand) { + // Open the Command + Gui::Command::openCommand("Add auto constraints"); + } + // Iterate through constraints std::vector::const_iterator it = autoConstrs.begin(); for (; it != autoConstrs.end(); ++it) { @@ -512,7 +515,9 @@ void DrawSketchHandler::createAutoConstraints(const std::vector break; } - Gui::Command::commitCommand(); + if(createowncommand) { + Gui::Command::commitCommand(); + } //Gui::Command::updateActive(); // There is already an recompute in each command creation, this is redundant. } } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.h b/src/Mod/Sketcher/Gui/DrawSketchHandler.h index b71337405..cbdd47a14 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.h @@ -82,8 +82,10 @@ public: int seekAutoConstraint(std::vector &suggestedConstraints, const Base::Vector2d &Pos, const Base::Vector2d &Dir, AutoConstraint::TargetType type = AutoConstraint::VERTEX); + // createowncommand indicates whether a separate command shall be create and committed (for example for undo purposes) or not + // is not it is the responsibility of the developer to create and commit the command appropriately. void createAutoConstraints(const std::vector &autoConstrs, - int geoId, Sketcher::PointPos pointPos=Sketcher::none); + int geoId, Sketcher::PointPos pointPos=Sketcher::none, bool createowncommand = true); void setPositionText(const Base::Vector2d &Pos, const SbString &text); void setPositionText(const Base::Vector2d &Pos);