1
/***************************************************************************
2
* Copyright (c) 2017 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
25
#include <Inventor/SbString.h>
26
#include <QApplication>
30
#include <App/Application.h>
31
#include <Base/Console.h>
32
#include <Gui/Action.h>
33
#include <Gui/Application.h>
34
#include <Gui/BitmapFactory.h>
35
#include <Gui/CommandT.h>
36
#include <Gui/Document.h>
37
#include <Gui/MainWindow.h>
38
#include <Gui/Notifications.h>
39
#include <Gui/Selection.h>
40
#include <Gui/SelectionObject.h>
41
#include <Mod/Sketcher/App/SketchObject.h>
43
#include "DrawSketchHandler.h"
45
#include "ViewProviderSketch.h"
49
using namespace SketcherGui;
50
using namespace Sketcher;
52
void ActivateBSplineHandler(Gui::Document* doc, DrawSketchHandler* handler)
54
std::unique_ptr<DrawSketchHandler> ptr(handler);
57
&& doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
58
SketcherGui::ViewProviderSketch* vp =
59
static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
61
vp->activateHandler(std::move(ptr));
66
/// For a knot given by (GeoId, PosId) finds the B-Spline and the knot's
67
/// index within it (by OCC numbering).
68
/// Returns true if the entities are found, false otherwise.
69
/// If returns false, `splineGeoId` and `knotIndexOCC` have garbage values.
70
bool findBSplineAndKnotIndex(Sketcher::SketchObject* Obj,
72
Sketcher::PointPos knotPosId,
76
for (auto const constraint : Obj->Constraints.getValues()) {
77
if (constraint->Type == Sketcher::InternalAlignment && constraint->First == knotGeoId
78
&& constraint->AlignmentType == Sketcher::BSplineKnotPoint) {
79
splineGeoId = constraint->Second;
80
knotIndexOCC = constraint->InternalAlignmentIndex + 1;
81
return true; // we have already found our knot.
85
// TODO: what to do if multiple splines have the same first/last point?
86
const Part::Geometry* geo = Obj->getGeometry(knotGeoId);
87
if (geo->is<Part::GeomBSplineCurve>()) {
88
splineGeoId = knotGeoId;
90
case Sketcher::PointPos::start:
93
case Sketcher::PointPos::end:
94
knotIndexOCC = static_cast<const Part::GeomBSplineCurve*>(geo)->countKnots();
97
// If we reach here something went wrong.
98
// isBsplineKnotOrEndPoint (that we expect is run before) will
99
// only accept spline knotGeoID if knotPosId is start or end.
108
DEF_STD_CMD_A(CmdSketcherConvertToNURBS)
110
CmdSketcherConvertToNURBS::CmdSketcherConvertToNURBS()
111
: Command("Sketcher_BSplineConvertToNURBS")
113
sAppModule = "Sketcher";
115
sMenuText = QT_TR_NOOP("Convert geometry to B-spline");
116
sToolTipText = QT_TR_NOOP("Converts the selected geometry to a B-spline");
117
sWhatsThis = "Sketcher_BSplineConvertToNURBS";
118
sStatusTip = sToolTipText;
119
sPixmap = "Sketcher_BSplineConvertToNURBS";
124
void CmdSketcherConvertToNURBS::activated(int iMsg)
129
std::vector<Gui::SelectionObject> selection;
130
selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
132
// only one sketch with its subelements are allowed to be selected
133
if (selection.size() != 1) {
137
// get the needed lists and objects
138
const std::vector<std::string>& SubNames = selection[0].getSubNames();
139
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
141
openCommand(QT_TRANSLATE_NOOP("Command", "Convert to NURBS"));
143
std::vector<int> GeoIdList;
145
for (const auto& subName : SubNames) {
147
if (subName.size() > 4 && subName.substr(0, 4) == "Edge") {
148
int GeoId = std::atoi(subName.substr(4, 4000).c_str()) - 1;
149
GeoIdList.push_back(GeoId);
151
else if (subName.size() > 12 && subName.substr(0, 12) == "ExternalEdge") {
152
int GeoId = -(std::atoi(subName.substr(12, 4000).c_str()) + 2);
153
GeoIdList.push_back(GeoId);
157
// for creating the poles and knots
158
for (auto GeoId : GeoIdList) {
159
Gui::cmdAppObjectArgs(selection[0].getObject(), "convertToNURBS(%d) ", GeoId);
161
for (auto GeoId : GeoIdList) {
162
Gui::cmdAppObjectArgs(selection[0].getObject(), "exposeInternalGeometry(%d)", GeoId);
165
if (GeoIdList.empty()) {
168
Gui::TranslatedUserWarning(Obj,
169
QObject::tr("Wrong selection"),
170
QObject::tr("None of the selected elements is an edge."));
175
tryAutoRecomputeIfNotSolve(Obj);
178
bool CmdSketcherConvertToNURBS::isActive()
180
return isSketcherBSplineActive(getActiveGuiDocument(), true);
183
// Increase degree of the spline
184
DEF_STD_CMD_A(CmdSketcherIncreaseDegree)
186
CmdSketcherIncreaseDegree::CmdSketcherIncreaseDegree()
187
: Command("Sketcher_BSplineIncreaseDegree")
189
sAppModule = "Sketcher";
191
sMenuText = QT_TR_NOOP("Increase B-spline degree");
192
sToolTipText = QT_TR_NOOP("Increases the degree of the B-spline");
193
sWhatsThis = "Sketcher_BSplineIncreaseDegree";
194
sStatusTip = sToolTipText;
195
sPixmap = "Sketcher_BSplineIncreaseDegree";
200
void CmdSketcherIncreaseDegree::activated(int iMsg)
205
std::vector<Gui::SelectionObject> selection;
206
selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
208
// only one sketch with its subelements are allowed to be selected
209
if (selection.size() != 1) {
213
// get the needed lists and objects
214
const std::vector<std::string>& SubNames = selection[0].getSubNames();
215
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
217
openCommand(QT_TRANSLATE_NOOP("Command", "Increase B-spline degree"));
219
bool ignored = false;
221
for (size_t i = 0; i < SubNames.size(); i++) {
223
if (SubNames[i].size() > 4 && SubNames[i].substr(0, 4) == "Edge") {
224
int GeoId = std::atoi(SubNames[i].substr(4, 4000).c_str()) - 1;
225
const Part::Geometry* geo = Obj->getGeometry(GeoId);
227
if (geo->is<Part::GeomBSplineCurve>()) {
228
Gui::cmdAppObjectArgs(selection[0].getObject(),
229
"increaseBSplineDegree(%d) ",
231
// add new control points
232
Gui::cmdAppObjectArgs(selection[0].getObject(),
233
"exposeInternalGeometry(%d)",
243
Gui::TranslatedUserWarning(Obj,
244
QObject::tr("Wrong selection"),
245
QObject::tr("At least one of the selected "
246
"objects was not a B-spline and was ignored."));
250
tryAutoRecomputeIfNotSolve(Obj);
251
getSelection().clearSelection();
254
bool CmdSketcherIncreaseDegree::isActive()
256
return isSketcherBSplineActive(getActiveGuiDocument(), true);
260
// Decrease degree of the spline
261
DEF_STD_CMD_A(CmdSketcherDecreaseDegree)
263
CmdSketcherDecreaseDegree::CmdSketcherDecreaseDegree()
264
: Command("Sketcher_BSplineDecreaseDegree")
266
sAppModule = "Sketcher";
268
sMenuText = QT_TR_NOOP("Decrease B-spline degree");
269
sToolTipText = QT_TR_NOOP("Decreases the degree of the B-spline");
270
sWhatsThis = "Sketcher_BSplineDecreaseDegree";
271
sStatusTip = sToolTipText;
272
sPixmap = "Sketcher_BSplineDecreaseDegree";
277
void CmdSketcherDecreaseDegree::activated(int iMsg)
282
std::vector<Gui::SelectionObject> selection;
283
selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
285
// only one sketch with its subelements are allowed to be selected
286
if (selection.size() != 1) {
290
getSelection().clearSelection();
292
// get the needed lists and objects
293
const std::vector<std::string>& SubNames = selection[0].getSubNames();
294
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
296
openCommand(QT_TRANSLATE_NOOP("Command", "Decrease B-spline degree"));
298
bool ignored = false;
300
for (size_t i = 0; i < SubNames.size(); i++) {
302
if (SubNames[i].size() > 4 && SubNames[i].substr(0, 4) == "Edge") {
303
int GeoId = std::atoi(SubNames[i].substr(4, 4000).c_str()) - 1;
304
const Part::Geometry* geo = Obj->getGeometry(GeoId);
306
if (geo->is<Part::GeomBSplineCurve>()) {
307
Gui::cmdAppObjectArgs(selection[0].getObject(),
308
"decreaseBSplineDegree(%d) ",
310
// add new control points
311
// Currently exposeInternalGeometry is called from within decreaseBSplineDegree
312
// because the old spline is deleted and a new one is added so that the GeoId is
313
// invalid afterwards
314
// Gui::cmdAppObjectArgs(selection[0].getObject(), "exposeInternalGeometry(%d)",
316
break; // cannot handle more than spline because the GeoIds will be invalidated
317
// after the first change
326
Gui::TranslatedUserWarning(Obj,
327
QObject::tr("Wrong selection"),
328
QObject::tr("At least one of the selected "
329
"objects was not a B-spline and was ignored."));
333
tryAutoRecomputeIfNotSolve(Obj);
334
getSelection().clearSelection();
337
bool CmdSketcherDecreaseDegree::isActive()
339
return isSketcherBSplineActive(getActiveGuiDocument(), true);
343
DEF_STD_CMD_A(CmdSketcherIncreaseKnotMultiplicity)
345
CmdSketcherIncreaseKnotMultiplicity::CmdSketcherIncreaseKnotMultiplicity()
346
: Command("Sketcher_BSplineIncreaseKnotMultiplicity")
348
sAppModule = "Sketcher";
350
sMenuText = QT_TR_NOOP("Increase knot multiplicity");
351
sToolTipText = QT_TR_NOOP("Increases the multiplicity of the selected knot of a B-spline");
352
sWhatsThis = "Sketcher_BSplineIncreaseKnotMultiplicity";
353
sStatusTip = sToolTipText;
354
sPixmap = "Sketcher_BSplineIncreaseKnotMultiplicity";
359
void CmdSketcherIncreaseKnotMultiplicity::activated(int iMsg)
364
std::vector<Gui::SelectionObject> selection;
365
selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
367
// only one sketch with its subelements are allowed to be selected
368
if (selection.size() != 1) {
372
// get the needed lists and objects
373
const std::vector<std::string>& SubNames = selection[0].getSubNames();
375
if (SubNames.size() > 1) {
376
// Check that only one object is selected,
377
// as we need only one object to get the new GeoId after multiplicity change
378
Gui::TranslatedUserWarning(
379
getActiveGuiDocument()->getDocument(),
380
QObject::tr("Wrong selection"),
382
"The selection comprises more than one item. Please select just one knot."));
386
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
388
openCommand(QT_TRANSLATE_NOOP("Command", "Increase knot multiplicity"));
391
Sketcher::PointPos PosId;
392
getIdsFromName(SubNames[0], Obj, GeoId, PosId);
397
bool applied = false;
398
bool notaknot = !(isBsplineKnotOrEndPoint(Obj, GeoId, PosId)
399
&& findBSplineAndKnotIndex(Obj, GeoId, PosId, splineGeoId, knotIndexOCC));
400
boost::uuids::uuid bsplinetag;
403
bsplinetag = Obj->getGeometry(splineGeoId)->getTag();
406
Gui::cmdAppObjectArgs(selection[0].getObject(),
407
"modifyBSplineKnotMultiplicity(%d, %d, %d) ",
413
// Warning: GeoId list might have changed
414
// as the consequence of deleting pole circles and
415
// particularly B-spline GeoID might have changed.
417
catch (const Base::CADKernelError& e) {
419
if (e.getTranslatable()) {
420
Gui::TranslatedUserError(Obj,
421
QObject::tr("CAD Kernel Error"),
422
QObject::tr(e.getMessage().c_str()));
424
getSelection().clearSelection();
426
catch (const Base::Exception& e) {
428
if (e.getTranslatable()) {
429
Gui::TranslatedUserError(Obj,
430
QObject::tr("Input Error"),
431
QObject::tr(e.getMessage().c_str()));
433
getSelection().clearSelection();
438
Gui::TranslatedUserWarning(
440
QObject::tr("Wrong selection"),
441
QObject::tr("None of the selected elements is a knot of a B-spline"));
445
// find new geoid for B-spline as GeoId might have changed
446
const std::vector<Part::Geometry*>& gvals = Obj->getInternalGeometry();
449
bool ngfound = false;
451
for (std::vector<Part::Geometry*>::const_iterator geo = gvals.begin(); geo != gvals.end();
453
if ((*geo) && (*geo)->getTag() == bsplinetag) {
461
// add internalalignment for new pole
462
Gui::cmdAppObjectArgs(selection[0].getObject(),
463
"exposeInternalGeometry(%d)",
466
catch (const Base::Exception& e) {
467
Gui::NotifyUserError(Obj,
468
QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
470
getSelection().clearSelection();
482
tryAutoRecomputeIfNotSolve(Obj);
483
getSelection().clearSelection();
486
bool CmdSketcherIncreaseKnotMultiplicity::isActive()
488
return isSketcherBSplineActive(getActiveGuiDocument(), true);
491
DEF_STD_CMD_A(CmdSketcherDecreaseKnotMultiplicity)
493
CmdSketcherDecreaseKnotMultiplicity::CmdSketcherDecreaseKnotMultiplicity()
494
: Command("Sketcher_BSplineDecreaseKnotMultiplicity")
496
sAppModule = "Sketcher";
498
sMenuText = QT_TR_NOOP("Decrease knot multiplicity");
499
sToolTipText = QT_TR_NOOP("Decreases the multiplicity of the selected knot of a B-spline");
500
sWhatsThis = "Sketcher_BSplineDecreaseKnotMultiplicity";
501
sStatusTip = sToolTipText;
502
sPixmap = "Sketcher_BSplineDecreaseKnotMultiplicity";
507
void CmdSketcherDecreaseKnotMultiplicity::activated(int iMsg)
512
std::vector<Gui::SelectionObject> selection;
513
selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
515
// only one sketch with its subelements are allowed to be selected
516
if (selection.size() != 1) {
520
// get the needed lists and objects
521
const std::vector<std::string>& SubNames = selection[0].getSubNames();
523
if (SubNames.size() > 1) {
524
// Check that only one object is selected,
525
// as we need only one object to get the new GeoId after multiplicity change
526
Gui::TranslatedUserWarning(
527
getActiveGuiDocument()->getDocument(),
528
QObject::tr("Wrong selection"),
530
"The selection comprises more than one item. Please select just one knot."));
534
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
536
openCommand(QT_TRANSLATE_NOOP("Command", "Decrease knot multiplicity"));
539
Sketcher::PointPos PosId;
540
getIdsFromName(SubNames[0], Obj, GeoId, PosId);
545
bool applied = false;
546
bool notaknot = !(isBsplineKnotOrEndPoint(Obj, GeoId, PosId)
547
&& findBSplineAndKnotIndex(Obj, GeoId, PosId, splineGeoId, knotIndexOCC));
548
boost::uuids::uuid bsplinetag;
551
bsplinetag = Obj->getGeometry(splineGeoId)->getTag();
554
Gui::cmdAppObjectArgs(selection[0].getObject(),
555
"modifyBSplineKnotMultiplicity(%d, %d, %d) ",
561
// Warning: GeoId list might have changed as the consequence of deleting pole circles
562
// and particularly B-spline GeoID might have changed.
564
catch (const Base::Exception& e) {
565
Gui::TranslatedUserError(Obj,
566
QObject::tr("Error"),
567
QObject::tr(getStrippedPythonExceptionString(e).c_str()));
569
getSelection().clearSelection();
574
Gui::TranslatedUserWarning(
576
QObject::tr("Wrong selection"),
577
QObject::tr("None of the selected elements is a knot of a B-spline"));
581
// find new geoid for B-spline as GeoId might have changed
582
const std::vector<Part::Geometry*>& gvals = Obj->getInternalGeometry();
585
bool ngfound = false;
587
for (std::vector<Part::Geometry*>::const_iterator geo = gvals.begin(); geo != gvals.end();
589
if ((*geo) && (*geo)->getTag() == bsplinetag) {
597
// add internalalignment for new pole
598
Gui::cmdAppObjectArgs(selection[0].getObject(),
599
"exposeInternalGeometry(%d)",
602
catch (const Base::Exception& e) {
603
Gui::NotifyUserError(Obj,
604
QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
606
getSelection().clearSelection();
618
tryAutoRecomputeIfNotSolve(Obj);
619
getSelection().clearSelection();
622
bool CmdSketcherDecreaseKnotMultiplicity::isActive()
624
return isSketcherBSplineActive(getActiveGuiDocument(), true);
628
// Composite drop down for knot increase/decrease
629
DEF_STD_CMD_ACLU(CmdSketcherCompModifyKnotMultiplicity)
631
CmdSketcherCompModifyKnotMultiplicity::CmdSketcherCompModifyKnotMultiplicity()
632
: Command("Sketcher_CompModifyKnotMultiplicity")
634
sAppModule = "Sketcher";
636
sMenuText = QT_TR_NOOP("Modify knot multiplicity");
637
sToolTipText = QT_TR_NOOP("Modifies the multiplicity of the selected knot of a B-spline");
638
sWhatsThis = "Sketcher_CompModifyKnotMultiplicity";
639
sStatusTip = sToolTipText;
643
void CmdSketcherCompModifyKnotMultiplicity::activated(int iMsg)
646
Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
650
cmd = rcCmdMgr.getCommandByName("Sketcher_BSplineIncreaseKnotMultiplicity");
652
else if (iMsg == 1) {
653
cmd = rcCmdMgr.getCommandByName("Sketcher_BSplineDecreaseKnotMultiplicity");
661
// Since the default icon is reset when enabling/disabling the command we have
662
// to explicitly set the icon of the used command.
663
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
664
QList<QAction*> a = pcAction->actions();
666
assert(iMsg < a.size());
667
pcAction->setIcon(a[iMsg]->icon());
670
Gui::Action* CmdSketcherCompModifyKnotMultiplicity::createAction()
672
Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
673
pcAction->setDropDownMenu(true);
674
applyCommandData(this->className(), pcAction);
676
QAction* c1 = pcAction->addAction(QString());
677
c1->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_BSplineIncreaseKnotMultiplicity"));
678
QAction* c2 = pcAction->addAction(QString());
679
c2->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_BSplineDecreaseKnotMultiplicity"));
681
_pcAction = pcAction;
684
pcAction->setIcon(c1->icon());
686
pcAction->setProperty("defaultAction", QVariant(defaultId));
691
void CmdSketcherCompModifyKnotMultiplicity::languageChange()
693
Command::languageChange();
698
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
699
QList<QAction*> a = pcAction->actions();
702
c1->setText(QApplication::translate("CmdSketcherCompModifyKnotMultiplicity",
703
"Increase knot multiplicity"));
705
QApplication::translate("Sketcher_BSplineIncreaseKnotMultiplicity",
706
"Increases the multiplicity of the selected knot of a B-spline"));
708
QApplication::translate("Sketcher_BSplineIncreaseKnotMultiplicity",
709
"Increases the multiplicity of the selected knot of a B-spline"));
711
c2->setText(QApplication::translate("CmdSketcherCompModifyKnotMultiplicity",
712
"Decrease knot multiplicity"));
714
QApplication::translate("Sketcher_BSplineDecreaseKnotMultiplicity",
715
"Decreases the multiplicity of the selected knot of a B-spline"));
717
QApplication::translate("Sketcher_BSplineDecreaseKnotMultiplicity",
718
"Decreases the multiplicity of the selected knot of a B-spline"));
721
void CmdSketcherCompModifyKnotMultiplicity::updateAction(int /*mode*/)
724
bool CmdSketcherCompModifyKnotMultiplicity::isActive()
726
return isSketcherBSplineActive(getActiveGuiDocument(), false);
729
class DrawSketchHandlerBSplineInsertKnot: public DrawSketchHandler
732
DrawSketchHandlerBSplineInsertKnot(Sketcher::SketchObject* _Obj, int _GeoId)
737
auto bsp = static_cast<const Part::GeomBSplineCurve*>(Obj->getGeometry(GeoId));
738
guessParam = bsp->getFirstParameter();
741
~DrawSketchHandlerBSplineInsertKnot() override
744
void mouseMove(Base::Vector2d onSketchPos) override
746
auto bsp = static_cast<const Part::GeomBSplineCurve*>(Obj->getGeometry(GeoId));
748
// get closest parameter using OCC
749
// TODO: This is called every time we move the cursor. Can get overwhelming.
750
Base::Vector3d onSketchPos3d(onSketchPos.x, onSketchPos.y, 0.0);
752
text.sprintf(" %.3f", guessParam);
753
// FIXME: Sometimes the "closest" point is on the other end of the B-Spline.
754
// Find when it happens and fix it?
755
bsp->closestParameter(onSketchPos3d, guessParam);
757
Base::Vector3d pointOnCurve3d = bsp->value(guessParam);
759
// TODO: Also draw a point at our position instead of just text
760
Base::Vector2d pointOnCurve(pointOnCurve3d.x, pointOnCurve3d.y);
761
setPositionText(pointOnCurve, text);
763
EditMarkers[0] = pointOnCurve;
764
drawEditMarkers(EditMarkers);
769
bool pressButton(Base::Vector2d /*onSketchPos*/) override
771
// just here to consume the button press
775
bool releaseButton(Base::Vector2d onSketchPos) override
777
Q_UNUSED(onSketchPos);
779
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert knot"));
781
bool applied = false;
782
boost::uuids::uuid bsplinetag = Obj->getGeometry(GeoId)->getTag();
785
Gui::cmdAppObjectArgs(Obj, "insertBSplineKnot(%d, %lf, %d) ", GeoId, guessParam, 1);
788
// Warning: GeoId list might have changed
789
// as the consequence of deleting pole circles and
790
// particularly B-spline GeoID might have changed.
792
catch (const Base::CADKernelError& e) {
794
if (e.getTranslatable()) {
795
Gui::TranslatedUserError(Obj,
796
QObject::tr("CAD Kernel Error"),
797
QObject::tr(e.getMessage().c_str()));
800
catch (const Base::Exception& e) {
802
if (e.getTranslatable()) {
803
Gui::TranslatedUserError(Obj,
804
QObject::tr("Input Error"),
805
QObject::tr(e.getMessage().c_str()));
810
bool newGeoIdFound = false;
813
// find new geoid for B-spline as GeoId might have changed
814
const std::vector<Part::Geometry*>& gvals = Obj->getInternalGeometry();
816
for (std::vector<Part::Geometry*>::const_iterator geo = gvals.begin();
819
if ((*geo) && (*geo)->getTag() == bsplinetag) {
820
newGeoIdFound = true;
827
// add internalalignment for new pole
828
Gui::cmdAppObjectArgs(Obj, "exposeInternalGeometry(%d)", newGeoId);
830
catch (const Base::Exception& e) {
831
Gui::NotifyUserError(Obj,
832
QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
839
Gui::Command::commitCommand();
842
Gui::Command::abortCommand();
845
tryAutoRecomputeIfNotSolve(Obj);
847
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
848
"User parameter:BaseApp/Preferences/Mod/Sketcher");
849
bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true);
850
if (continuousMode && newGeoIdFound) {
851
// This code enables the continuous creation mode.
853
// The new entities created changed the B-Spline's GeoId
857
/* It is ok not to call to purgeHandler
858
* in continuous creation mode because the
859
* handler is destroyed by the quit() method on pressing the
860
* right button of the mouse */
864
->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
871
QString getCrosshairCursorSVGName() const override
873
return QString::fromLatin1("Sketcher_Pointer_InsertKnot");
877
Sketcher::SketchObject* Obj;
880
std::vector<Base::Vector2d> EditMarkers;
883
DEF_STD_CMD_A(CmdSketcherInsertKnot)
885
CmdSketcherInsertKnot::CmdSketcherInsertKnot()
886
: Command("Sketcher_BSplineInsertKnot")
888
sAppModule = "Sketcher";
890
sMenuText = QT_TR_NOOP("Insert knot");
891
sToolTipText = QT_TR_NOOP("Inserts knot at given parameter. If a knot already exists at that "
892
"parameter, it's multiplicity is increased by one.");
893
sWhatsThis = "Sketcher_BSplineInsertKnot";
894
sStatusTip = sToolTipText;
895
sPixmap = "Sketcher_BSplineInsertKnot";
900
void CmdSketcherInsertKnot::activated(int iMsg)
905
std::vector<Gui::SelectionObject> selection;
906
selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
908
// TODO: let user click on a curve after pressing command.
909
// only one sketch with its subelements are allowed to be selected
910
if (selection.size() != 1) {
914
// get the needed lists and objects
915
const std::vector<std::string>& SubNames = selection[0].getSubNames();
916
if (SubNames.empty()) {
917
// Check that only one object is selected,
918
// as we need only one object to get the new GeoId after multiplicity change
919
Gui::TranslatedUserWarning(getActiveGuiDocument()->getDocument(),
920
QObject::tr("Selection is empty"),
921
QObject::tr("Nothing is selected. Please select a B-spline."));
925
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
927
// TODO: Ensure GeoId is for the BSpline and not for it's internal geometry
928
int GeoId = std::atoi(SubNames[0].substr(4, 4000).c_str()) - 1;
929
const Part::Geometry* geo = Obj->getGeometry(GeoId);
931
if (geo->is<Part::GeomBSplineCurve>()) {
932
ActivateBSplineHandler(getActiveGuiDocument(),
933
new DrawSketchHandlerBSplineInsertKnot(Obj, GeoId));
936
Gui::TranslatedUserWarning(
938
QObject::tr("Wrong selection"),
939
QObject::tr("Please select a B-spline to insert a knot (not a knot on it). "
940
"If the curve is not a B-spline, please convert it into one first."));
943
getSelection().clearSelection();
946
bool CmdSketcherInsertKnot::isActive()
948
return isSketcherBSplineActive(getActiveGuiDocument(), true);
951
DEF_STD_CMD_A(CmdSketcherJoinCurves)
953
CmdSketcherJoinCurves::CmdSketcherJoinCurves()
954
: Command("Sketcher_JoinCurves")
956
sAppModule = "Sketcher";
958
sMenuText = QT_TR_NOOP("Join curves");
959
sToolTipText = QT_TR_NOOP("Join two curves at selected end points");
960
sWhatsThis = "Sketcher_JoinCurves";
961
sStatusTip = sToolTipText;
962
sPixmap = "Sketcher_JoinCurves";
967
void CmdSketcherJoinCurves::activated(int iMsg)
972
std::vector<Gui::SelectionObject> selection;
973
selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
975
// only one sketch with its subelements are allowed to be selected
976
if (selection.size() != 1) {
980
// get the needed lists and objects
981
const std::vector<std::string>& SubNames = selection[0].getSubNames();
984
Sketcher::PointPos PosIds[2];
986
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
988
switch (SubNames.size()) {
990
// Nothing is selected
991
Gui::TranslatedUserWarning(
993
QObject::tr("Selection is empty"),
994
QObject::tr("Nothing is selected. Please select end points of curves."));
998
std::vector<int> GeoIdList;
999
std::vector<Sketcher::PointPos> PosIdList;
1002
Sketcher::PointPos selPosId;
1004
getIdsFromName(SubNames[0], Obj, selGeoId, selPosId);
1006
Obj->getDirectlyCoincidentPoints(selGeoId, selPosId, GeoIdList, PosIdList);
1008
// Find the right pair of coincident points
1010
for (size_t i = 0; i < GeoIdList.size(); ++i) {
1011
if (Sketcher::PointPos::start == PosIdList[i]
1012
|| Sketcher::PointPos::end == PosIdList[i]) {
1014
GeoIds[j] = GeoIdList[i];
1015
PosIds[j] = PosIdList[i];
1019
Gui::TranslatedUserWarning(
1021
QObject::tr("Too many curves on point"),
1022
QObject::tr("Exactly two curves should end at the selected point to be "
1023
"able to join them."));
1030
Gui::TranslatedUserWarning(Obj,
1031
QObject::tr("Too few curves on point"),
1032
QObject::tr("Exactly two curves should end at the "
1033
"selected point to be able to join them."));
1041
getIdsFromName(SubNames[0], Obj, GeoIds[0], PosIds[0]);
1042
getIdsFromName(SubNames[1], Obj, GeoIds[1], PosIds[1]);
1046
Gui::TranslatedUserWarning(
1048
QObject::tr("Wrong selection"),
1049
QObject::tr("Two end points, or coincident point should be selected."));
1055
// TODO: Check for tangent constraints between these the two points.
1056
// These need to be explicit: indirect tangency because poles are collinear will not work.
1057
bool tangentConstraintExists = false;
1058
for (const auto& constr : Obj->Constraints.getValues()) {
1059
if (constr->Type == Sketcher::ConstraintType::Tangent
1060
&& ((constr->First == GeoIds[0] && constr->FirstPos == PosIds[0]
1061
&& constr->Second == GeoIds[1] && constr->SecondPos == PosIds[1])
1062
|| (constr->First == GeoIds[1] && constr->FirstPos == PosIds[1]
1063
&& constr->Second == GeoIds[0] && constr->SecondPos == PosIds[0]))) {
1064
tangentConstraintExists = true;
1068
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Join Curves"));
1069
bool applied = false;
1072
Gui::cmdAppObjectArgs(selection[0].getObject(),
1073
"join(%d, %d, %d, %d, %d) ",
1075
static_cast<int>(PosIds[0]),
1077
static_cast<int>(PosIds[1]),
1078
tangentConstraintExists ? 1 : 0);
1081
// Warning: GeoId list will have changed
1083
catch (const Base::Exception& e) {
1084
Gui::TranslatedUserError(Obj,
1085
QObject::tr("Error"),
1086
QObject::tr(getStrippedPythonExceptionString(e).c_str()));
1088
getSelection().clearSelection();
1092
Gui::Command::commitCommand();
1095
Gui::Command::abortCommand();
1098
tryAutoRecomputeIfNotSolve(Obj);
1099
getSelection().clearSelection();
1102
bool CmdSketcherJoinCurves::isActive()
1104
return isSketcherBSplineActive(getActiveGuiDocument(), true);
1107
void CreateSketcherCommandsBSpline()
1109
Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
1111
rcCmdMgr.addCommand(new CmdSketcherConvertToNURBS());
1112
rcCmdMgr.addCommand(new CmdSketcherIncreaseDegree());
1113
rcCmdMgr.addCommand(new CmdSketcherDecreaseDegree());
1114
rcCmdMgr.addCommand(new CmdSketcherIncreaseKnotMultiplicity());
1115
rcCmdMgr.addCommand(new CmdSketcherDecreaseKnotMultiplicity());
1116
rcCmdMgr.addCommand(new CmdSketcherCompModifyKnotMultiplicity());
1117
rcCmdMgr.addCommand(new CmdSketcherInsertKnot());
1118
rcCmdMgr.addCommand(new CmdSketcherJoinCurves());