FreeCAD

Форк
0
/
CommandSketcherBSpline.cpp 
1119 строк · 38.6 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2017 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com>     *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
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.      *
10
 *                                                                         *
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.                  *
15
 *                                                                         *
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                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24
#ifndef _PreComp_
25
#include <Inventor/SbString.h>
26
#include <QApplication>
27
#include <cfloat>
28
#endif
29

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

43
#include "DrawSketchHandler.h"
44
#include "Utils.h"
45
#include "ViewProviderSketch.h"
46

47

48
using namespace std;
49
using namespace SketcherGui;
50
using namespace Sketcher;
51

52
void ActivateBSplineHandler(Gui::Document* doc, DrawSketchHandler* handler)
53
{
54
    std::unique_ptr<DrawSketchHandler> ptr(handler);
55
    if (doc) {
56
        if (doc->getInEdit()
57
            && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
58
            SketcherGui::ViewProviderSketch* vp =
59
                static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
60
            vp->purgeHandler();
61
            vp->activateHandler(std::move(ptr));
62
        }
63
    }
64
}
65

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,
71
                             int knotGeoId,
72
                             Sketcher::PointPos knotPosId,
73
                             int& splineGeoId,
74
                             int& knotIndexOCC)
75
{
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.
82
        }
83
    }
84

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;
89
        switch (knotPosId) {
90
            case Sketcher::PointPos::start:
91
                knotIndexOCC = 1;
92
                return true;
93
            case Sketcher::PointPos::end:
94
                knotIndexOCC = static_cast<const Part::GeomBSplineCurve*>(geo)->countKnots();
95
                return true;
96
            default:
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.
100
                return false;
101
        }
102
    }
103

104
    return false;
105
}
106

107
// Convert to NURBS
108
DEF_STD_CMD_A(CmdSketcherConvertToNURBS)
109

110
CmdSketcherConvertToNURBS::CmdSketcherConvertToNURBS()
111
    : Command("Sketcher_BSplineConvertToNURBS")
112
{
113
    sAppModule = "Sketcher";
114
    sGroup = "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";
120
    sAccel = "";
121
    eType = ForEdit;
122
}
123

124
void CmdSketcherConvertToNURBS::activated(int iMsg)
125
{
126
    Q_UNUSED(iMsg);
127

128
    // get the selection
129
    std::vector<Gui::SelectionObject> selection;
130
    selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
131

132
    // only one sketch with its subelements are allowed to be selected
133
    if (selection.size() != 1) {
134
        return;
135
    }
136

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());
140

141
    openCommand(QT_TRANSLATE_NOOP("Command", "Convert to NURBS"));
142

143
    std::vector<int> GeoIdList;
144

145
    for (const auto& subName : SubNames) {
146
        // only handle edges
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);
150
        }
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);
154
        }
155
    }
156

157
    // for creating the poles and knots
158
    for (auto GeoId : GeoIdList) {
159
        Gui::cmdAppObjectArgs(selection[0].getObject(), "convertToNURBS(%d) ", GeoId);
160
    }
161
    for (auto GeoId : GeoIdList) {
162
        Gui::cmdAppObjectArgs(selection[0].getObject(), "exposeInternalGeometry(%d)", GeoId);
163
    }
164

165
    if (GeoIdList.empty()) {
166
        abortCommand();
167

168
        Gui::TranslatedUserWarning(Obj,
169
                                   QObject::tr("Wrong selection"),
170
                                   QObject::tr("None of the selected elements is an edge."));
171
    }
172
    else {
173
        commitCommand();
174
    }
175
    tryAutoRecomputeIfNotSolve(Obj);
176
}
177

178
bool CmdSketcherConvertToNURBS::isActive()
179
{
180
    return isSketcherBSplineActive(getActiveGuiDocument(), true);
181
}
182

183
// Increase degree of the spline
184
DEF_STD_CMD_A(CmdSketcherIncreaseDegree)
185

186
CmdSketcherIncreaseDegree::CmdSketcherIncreaseDegree()
187
    : Command("Sketcher_BSplineIncreaseDegree")
188
{
189
    sAppModule = "Sketcher";
190
    sGroup = "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";
196
    sAccel = "";
197
    eType = ForEdit;
198
}
199

200
void CmdSketcherIncreaseDegree::activated(int iMsg)
201
{
202
    Q_UNUSED(iMsg);
203

204
    // get the selection
205
    std::vector<Gui::SelectionObject> selection;
206
    selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
207

208
    // only one sketch with its subelements are allowed to be selected
209
    if (selection.size() != 1) {
210
        return;
211
    }
212

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());
216

217
    openCommand(QT_TRANSLATE_NOOP("Command", "Increase B-spline degree"));
218

219
    bool ignored = false;
220

221
    for (size_t i = 0; i < SubNames.size(); i++) {
222
        // only handle edges
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);
226

227
            if (geo->is<Part::GeomBSplineCurve>()) {
228
                Gui::cmdAppObjectArgs(selection[0].getObject(),
229
                                      "increaseBSplineDegree(%d) ",
230
                                      GeoId);
231
                // add new control points
232
                Gui::cmdAppObjectArgs(selection[0].getObject(),
233
                                      "exposeInternalGeometry(%d)",
234
                                      GeoId);
235
            }
236
            else {
237
                ignored = true;
238
            }
239
        }
240
    }
241

242
    if (ignored) {
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."));
247
    }
248

249
    commitCommand();
250
    tryAutoRecomputeIfNotSolve(Obj);
251
    getSelection().clearSelection();
252
}
253

254
bool CmdSketcherIncreaseDegree::isActive()
255
{
256
    return isSketcherBSplineActive(getActiveGuiDocument(), true);
257
}
258

259

260
// Decrease degree of the spline
261
DEF_STD_CMD_A(CmdSketcherDecreaseDegree)
262

263
CmdSketcherDecreaseDegree::CmdSketcherDecreaseDegree()
264
    : Command("Sketcher_BSplineDecreaseDegree")
265
{
266
    sAppModule = "Sketcher";
267
    sGroup = "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";
273
    sAccel = "";
274
    eType = ForEdit;
275
}
276

277
void CmdSketcherDecreaseDegree::activated(int iMsg)
278
{
279
    Q_UNUSED(iMsg);
280

281
    // get the selection
282
    std::vector<Gui::SelectionObject> selection;
283
    selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
284

285
    // only one sketch with its subelements are allowed to be selected
286
    if (selection.size() != 1) {
287
        return;
288
    }
289

290
    getSelection().clearSelection();
291

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());
295

296
    openCommand(QT_TRANSLATE_NOOP("Command", "Decrease B-spline degree"));
297

298
    bool ignored = false;
299

300
    for (size_t i = 0; i < SubNames.size(); i++) {
301
        // only handle edges
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);
305

306
            if (geo->is<Part::GeomBSplineCurve>()) {
307
                Gui::cmdAppObjectArgs(selection[0].getObject(),
308
                                      "decreaseBSplineDegree(%d) ",
309
                                      GeoId);
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)",
315
                // GeoId);
316
                break;  // cannot handle more than spline because the GeoIds will be invalidated
317
                        // after the first change
318
            }
319
            else {
320
                ignored = true;
321
            }
322
        }
323
    }
324

325
    if (ignored) {
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."));
330
    }
331

332
    commitCommand();
333
    tryAutoRecomputeIfNotSolve(Obj);
334
    getSelection().clearSelection();
335
}
336

337
bool CmdSketcherDecreaseDegree::isActive()
338
{
339
    return isSketcherBSplineActive(getActiveGuiDocument(), true);
340
}
341

342

343
DEF_STD_CMD_A(CmdSketcherIncreaseKnotMultiplicity)
344

345
CmdSketcherIncreaseKnotMultiplicity::CmdSketcherIncreaseKnotMultiplicity()
346
    : Command("Sketcher_BSplineIncreaseKnotMultiplicity")
347
{
348
    sAppModule = "Sketcher";
349
    sGroup = "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";
355
    sAccel = "";
356
    eType = ForEdit;
357
}
358

359
void CmdSketcherIncreaseKnotMultiplicity::activated(int iMsg)
360
{
361
    Q_UNUSED(iMsg);
362

363
    // get the selection
364
    std::vector<Gui::SelectionObject> selection;
365
    selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
366

367
    // only one sketch with its subelements are allowed to be selected
368
    if (selection.size() != 1) {
369
        return;
370
    }
371

372
    // get the needed lists and objects
373
    const std::vector<std::string>& SubNames = selection[0].getSubNames();
374

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"),
381
            QObject::tr(
382
                "The selection comprises more than one item. Please select just one knot."));
383
        return;
384
    }
385

386
    Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
387

388
    openCommand(QT_TRANSLATE_NOOP("Command", "Increase knot multiplicity"));
389

390
    int GeoId;
391
    Sketcher::PointPos PosId;
392
    getIdsFromName(SubNames[0], Obj, GeoId, PosId);
393

394
    int splineGeoId;
395
    int knotIndexOCC;
396

397
    bool applied = false;
398
    bool notaknot = !(isBsplineKnotOrEndPoint(Obj, GeoId, PosId)
399
                      && findBSplineAndKnotIndex(Obj, GeoId, PosId, splineGeoId, knotIndexOCC));
400
    boost::uuids::uuid bsplinetag;
401

402
    if (!notaknot) {
403
        bsplinetag = Obj->getGeometry(splineGeoId)->getTag();
404

405
        try {
406
            Gui::cmdAppObjectArgs(selection[0].getObject(),
407
                                  "modifyBSplineKnotMultiplicity(%d, %d, %d) ",
408
                                  splineGeoId,
409
                                  knotIndexOCC,
410
                                  1);
411
            applied = true;
412

413
            // Warning: GeoId list might have changed
414
            // as the consequence of deleting pole circles and
415
            // particularly B-spline GeoID might have changed.
416
        }
417
        catch (const Base::CADKernelError& e) {
418
            e.ReportException();
419
            if (e.getTranslatable()) {
420
                Gui::TranslatedUserError(Obj,
421
                                         QObject::tr("CAD Kernel Error"),
422
                                         QObject::tr(e.getMessage().c_str()));
423
            }
424
            getSelection().clearSelection();
425
        }
426
        catch (const Base::Exception& e) {
427
            e.ReportException();
428
            if (e.getTranslatable()) {
429
                Gui::TranslatedUserError(Obj,
430
                                         QObject::tr("Input Error"),
431
                                         QObject::tr(e.getMessage().c_str()));
432
            }
433
            getSelection().clearSelection();
434
        }
435
    }
436

437
    if (notaknot) {
438
        Gui::TranslatedUserWarning(
439
            Obj,
440
            QObject::tr("Wrong selection"),
441
            QObject::tr("None of the selected elements is a knot of a B-spline"));
442
    }
443

444
    if (applied) {
445
        // find new geoid for B-spline as GeoId might have changed
446
        const std::vector<Part::Geometry*>& gvals = Obj->getInternalGeometry();
447

448
        int ngeoid = 0;
449
        bool ngfound = false;
450

451
        for (std::vector<Part::Geometry*>::const_iterator geo = gvals.begin(); geo != gvals.end();
452
             geo++, ngeoid++) {
453
            if ((*geo) && (*geo)->getTag() == bsplinetag) {
454
                ngfound = true;
455
                break;
456
            }
457
        }
458

459
        if (ngfound) {
460
            try {
461
                // add internalalignment for new pole
462
                Gui::cmdAppObjectArgs(selection[0].getObject(),
463
                                      "exposeInternalGeometry(%d)",
464
                                      ngeoid);
465
            }
466
            catch (const Base::Exception& e) {
467
                Gui::NotifyUserError(Obj,
468
                                     QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
469
                                     e.what());
470
                getSelection().clearSelection();
471
            }
472
        }
473
    }
474

475
    if (!applied) {
476
        abortCommand();
477
    }
478
    else {
479
        commitCommand();
480
    }
481

482
    tryAutoRecomputeIfNotSolve(Obj);
483
    getSelection().clearSelection();
484
}
485

486
bool CmdSketcherIncreaseKnotMultiplicity::isActive()
487
{
488
    return isSketcherBSplineActive(getActiveGuiDocument(), true);
489
}
490

491
DEF_STD_CMD_A(CmdSketcherDecreaseKnotMultiplicity)
492

493
CmdSketcherDecreaseKnotMultiplicity::CmdSketcherDecreaseKnotMultiplicity()
494
    : Command("Sketcher_BSplineDecreaseKnotMultiplicity")
495
{
496
    sAppModule = "Sketcher";
497
    sGroup = "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";
503
    sAccel = "";
504
    eType = ForEdit;
505
}
506

507
void CmdSketcherDecreaseKnotMultiplicity::activated(int iMsg)
508
{
509
    Q_UNUSED(iMsg);
510

511
    // get the selection
512
    std::vector<Gui::SelectionObject> selection;
513
    selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
514

515
    // only one sketch with its subelements are allowed to be selected
516
    if (selection.size() != 1) {
517
        return;
518
    }
519

520
    // get the needed lists and objects
521
    const std::vector<std::string>& SubNames = selection[0].getSubNames();
522

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"),
529
            QObject::tr(
530
                "The selection comprises more than one item. Please select just one knot."));
531
        return;
532
    }
533

534
    Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
535

536
    openCommand(QT_TRANSLATE_NOOP("Command", "Decrease knot multiplicity"));
537

538
    int GeoId;
539
    Sketcher::PointPos PosId;
540
    getIdsFromName(SubNames[0], Obj, GeoId, PosId);
541

542
    int splineGeoId;
543
    int knotIndexOCC;
544

545
    bool applied = false;
546
    bool notaknot = !(isBsplineKnotOrEndPoint(Obj, GeoId, PosId)
547
                      && findBSplineAndKnotIndex(Obj, GeoId, PosId, splineGeoId, knotIndexOCC));
548
    boost::uuids::uuid bsplinetag;
549

550
    if (!notaknot) {
551
        bsplinetag = Obj->getGeometry(splineGeoId)->getTag();
552

553
        try {
554
            Gui::cmdAppObjectArgs(selection[0].getObject(),
555
                                  "modifyBSplineKnotMultiplicity(%d, %d, %d) ",
556
                                  splineGeoId,
557
                                  knotIndexOCC,
558
                                  -1);
559
            applied = true;
560

561
            // Warning: GeoId list might have changed as the consequence of deleting pole circles
562
            // and particularly B-spline GeoID might have changed.
563
        }
564
        catch (const Base::Exception& e) {
565
            Gui::TranslatedUserError(Obj,
566
                                     QObject::tr("Error"),
567
                                     QObject::tr(getStrippedPythonExceptionString(e).c_str()));
568

569
            getSelection().clearSelection();
570
        }
571
    }
572

573
    if (notaknot) {
574
        Gui::TranslatedUserWarning(
575
            Obj,
576
            QObject::tr("Wrong selection"),
577
            QObject::tr("None of the selected elements is a knot of a B-spline"));
578
    }
579

580
    if (applied) {
581
        // find new geoid for B-spline as GeoId might have changed
582
        const std::vector<Part::Geometry*>& gvals = Obj->getInternalGeometry();
583

584
        int ngeoid = 0;
585
        bool ngfound = false;
586

587
        for (std::vector<Part::Geometry*>::const_iterator geo = gvals.begin(); geo != gvals.end();
588
             geo++, ngeoid++) {
589
            if ((*geo) && (*geo)->getTag() == bsplinetag) {
590
                ngfound = true;
591
                break;
592
            }
593
        }
594

595
        if (ngfound) {
596
            try {
597
                // add internalalignment for new pole
598
                Gui::cmdAppObjectArgs(selection[0].getObject(),
599
                                      "exposeInternalGeometry(%d)",
600
                                      ngeoid);
601
            }
602
            catch (const Base::Exception& e) {
603
                Gui::NotifyUserError(Obj,
604
                                     QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
605
                                     e.what());
606
                getSelection().clearSelection();
607
            }
608
        }
609
    }
610

611
    if (!applied) {
612
        abortCommand();
613
    }
614
    else {
615
        commitCommand();
616
    }
617

618
    tryAutoRecomputeIfNotSolve(Obj);
619
    getSelection().clearSelection();
620
}
621

622
bool CmdSketcherDecreaseKnotMultiplicity::isActive()
623
{
624
    return isSketcherBSplineActive(getActiveGuiDocument(), true);
625
}
626

627

628
// Composite drop down for knot increase/decrease
629
DEF_STD_CMD_ACLU(CmdSketcherCompModifyKnotMultiplicity)
630

631
CmdSketcherCompModifyKnotMultiplicity::CmdSketcherCompModifyKnotMultiplicity()
632
    : Command("Sketcher_CompModifyKnotMultiplicity")
633
{
634
    sAppModule = "Sketcher";
635
    sGroup = "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;
640
    eType = ForEdit;
641
}
642

643
void CmdSketcherCompModifyKnotMultiplicity::activated(int iMsg)
644
{
645

646
    Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
647
    Gui::Command* cmd;
648

649
    if (iMsg == 0) {
650
        cmd = rcCmdMgr.getCommandByName("Sketcher_BSplineIncreaseKnotMultiplicity");
651
    }
652
    else if (iMsg == 1) {
653
        cmd = rcCmdMgr.getCommandByName("Sketcher_BSplineDecreaseKnotMultiplicity");
654
    }
655
    else {
656
        return;
657
    }
658

659
    cmd->invoke(0);
660

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();
665

666
    assert(iMsg < a.size());
667
    pcAction->setIcon(a[iMsg]->icon());
668
}
669

670
Gui::Action* CmdSketcherCompModifyKnotMultiplicity::createAction()
671
{
672
    Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
673
    pcAction->setDropDownMenu(true);
674
    applyCommandData(this->className(), pcAction);
675

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"));
680

681
    _pcAction = pcAction;
682
    languageChange();
683

684
    pcAction->setIcon(c1->icon());
685
    int defaultId = 0;
686
    pcAction->setProperty("defaultAction", QVariant(defaultId));
687

688
    return pcAction;
689
}
690

691
void CmdSketcherCompModifyKnotMultiplicity::languageChange()
692
{
693
    Command::languageChange();
694

695
    if (!_pcAction) {
696
        return;
697
    }
698
    Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
699
    QList<QAction*> a = pcAction->actions();
700

701
    QAction* c1 = a[0];
702
    c1->setText(QApplication::translate("CmdSketcherCompModifyKnotMultiplicity",
703
                                        "Increase knot multiplicity"));
704
    c1->setToolTip(
705
        QApplication::translate("Sketcher_BSplineIncreaseKnotMultiplicity",
706
                                "Increases the multiplicity of the selected knot of a B-spline"));
707
    c1->setStatusTip(
708
        QApplication::translate("Sketcher_BSplineIncreaseKnotMultiplicity",
709
                                "Increases the multiplicity of the selected knot of a B-spline"));
710
    QAction* c2 = a[1];
711
    c2->setText(QApplication::translate("CmdSketcherCompModifyKnotMultiplicity",
712
                                        "Decrease knot multiplicity"));
713
    c2->setToolTip(
714
        QApplication::translate("Sketcher_BSplineDecreaseKnotMultiplicity",
715
                                "Decreases the multiplicity of the selected knot of a B-spline"));
716
    c2->setStatusTip(
717
        QApplication::translate("Sketcher_BSplineDecreaseKnotMultiplicity",
718
                                "Decreases the multiplicity of the selected knot of a B-spline"));
719
}
720

721
void CmdSketcherCompModifyKnotMultiplicity::updateAction(int /*mode*/)
722
{}
723

724
bool CmdSketcherCompModifyKnotMultiplicity::isActive()
725
{
726
    return isSketcherBSplineActive(getActiveGuiDocument(), false);
727
}
728

729
class DrawSketchHandlerBSplineInsertKnot: public DrawSketchHandler
730
{
731
public:
732
    DrawSketchHandlerBSplineInsertKnot(Sketcher::SketchObject* _Obj, int _GeoId)
733
        : Obj(_Obj)
734
        , GeoId(_GeoId)
735
        , EditMarkers(1)
736
    {
737
        auto bsp = static_cast<const Part::GeomBSplineCurve*>(Obj->getGeometry(GeoId));
738
        guessParam = bsp->getFirstParameter();
739
    }
740

741
    ~DrawSketchHandlerBSplineInsertKnot() override
742
    {}
743

744
    void mouseMove(Base::Vector2d onSketchPos) override
745
    {
746
        auto bsp = static_cast<const Part::GeomBSplineCurve*>(Obj->getGeometry(GeoId));
747

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);
751
        SbString text;
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);
756

757
        Base::Vector3d pointOnCurve3d = bsp->value(guessParam);
758

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);
762

763
        EditMarkers[0] = pointOnCurve;
764
        drawEditMarkers(EditMarkers);
765

766
        applyCursor();
767
    }
768

769
    bool pressButton(Base::Vector2d /*onSketchPos*/) override
770
    {
771
        // just here to consume the button press
772
        return true;
773
    }
774

775
    bool releaseButton(Base::Vector2d onSketchPos) override
776
    {
777
        Q_UNUSED(onSketchPos);
778

779
        Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert knot"));
780

781
        bool applied = false;
782
        boost::uuids::uuid bsplinetag = Obj->getGeometry(GeoId)->getTag();
783

784
        try {
785
            Gui::cmdAppObjectArgs(Obj, "insertBSplineKnot(%d, %lf, %d) ", GeoId, guessParam, 1);
786
            applied = true;
787

788
            // Warning: GeoId list might have changed
789
            // as the consequence of deleting pole circles and
790
            // particularly B-spline GeoID might have changed.
791
        }
792
        catch (const Base::CADKernelError& e) {
793
            e.ReportException();
794
            if (e.getTranslatable()) {
795
                Gui::TranslatedUserError(Obj,
796
                                         QObject::tr("CAD Kernel Error"),
797
                                         QObject::tr(e.getMessage().c_str()));
798
            }
799
        }
800
        catch (const Base::Exception& e) {
801
            e.ReportException();
802
            if (e.getTranslatable()) {
803
                Gui::TranslatedUserError(Obj,
804
                                         QObject::tr("Input Error"),
805
                                         QObject::tr(e.getMessage().c_str()));
806
            }
807
        }
808

809
        int newGeoId = 0;
810
        bool newGeoIdFound = false;
811

812
        if (applied) {
813
            // find new geoid for B-spline as GeoId might have changed
814
            const std::vector<Part::Geometry*>& gvals = Obj->getInternalGeometry();
815

816
            for (std::vector<Part::Geometry*>::const_iterator geo = gvals.begin();
817
                 geo != gvals.end();
818
                 geo++, newGeoId++) {
819
                if ((*geo) && (*geo)->getTag() == bsplinetag) {
820
                    newGeoIdFound = true;
821
                    break;
822
                }
823
            }
824

825
            if (newGeoIdFound) {
826
                try {
827
                    // add internalalignment for new pole
828
                    Gui::cmdAppObjectArgs(Obj, "exposeInternalGeometry(%d)", newGeoId);
829
                }
830
                catch (const Base::Exception& e) {
831
                    Gui::NotifyUserError(Obj,
832
                                         QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
833
                                         e.what());
834
                }
835
            }
836
        }
837

838
        if (applied) {
839
            Gui::Command::commitCommand();
840
        }
841
        else {
842
            Gui::Command::abortCommand();
843
        }
844

845
        tryAutoRecomputeIfNotSolve(Obj);
846

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.
852

853
            // The new entities created changed the B-Spline's GeoId
854
            GeoId = newGeoId;
855

856
            applyCursor();
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 */
861
        }
862
        else {
863
            sketchgui
864
                ->purgeHandler();  // no code after this line, Handler get deleted in ViewProvider
865
        }
866

867
        return true;
868
    }
869

870
private:
871
    QString getCrosshairCursorSVGName() const override
872
    {
873
        return QString::fromLatin1("Sketcher_Pointer_InsertKnot");
874
    }
875

876
protected:
877
    Sketcher::SketchObject* Obj;
878
    int GeoId;
879
    double guessParam;
880
    std::vector<Base::Vector2d> EditMarkers;
881
};
882

883
DEF_STD_CMD_A(CmdSketcherInsertKnot)
884

885
CmdSketcherInsertKnot::CmdSketcherInsertKnot()
886
    : Command("Sketcher_BSplineInsertKnot")
887
{
888
    sAppModule = "Sketcher";
889
    sGroup = "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";
896
    sAccel = "";
897
    eType = ForEdit;
898
}
899

900
void CmdSketcherInsertKnot::activated(int iMsg)
901
{
902
    Q_UNUSED(iMsg);
903

904
    // get the selection
905
    std::vector<Gui::SelectionObject> selection;
906
    selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
907

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) {
911
        return;
912
    }
913

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."));
922

923
        return;
924
    }
925
    Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
926

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);
930

931
    if (geo->is<Part::GeomBSplineCurve>()) {
932
        ActivateBSplineHandler(getActiveGuiDocument(),
933
                               new DrawSketchHandlerBSplineInsertKnot(Obj, GeoId));
934
    }
935
    else {
936
        Gui::TranslatedUserWarning(
937
            Obj,
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."));
941
    }
942

943
    getSelection().clearSelection();
944
}
945

946
bool CmdSketcherInsertKnot::isActive()
947
{
948
    return isSketcherBSplineActive(getActiveGuiDocument(), true);
949
}
950

951
DEF_STD_CMD_A(CmdSketcherJoinCurves)
952

953
CmdSketcherJoinCurves::CmdSketcherJoinCurves()
954
    : Command("Sketcher_JoinCurves")
955
{
956
    sAppModule = "Sketcher";
957
    sGroup = "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";
963
    sAccel = "";
964
    eType = ForEdit;
965
}
966

967
void CmdSketcherJoinCurves::activated(int iMsg)
968
{
969
    Q_UNUSED(iMsg);
970

971
    // get the selection
972
    std::vector<Gui::SelectionObject> selection;
973
    selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId());
974

975
    // only one sketch with its subelements are allowed to be selected
976
    if (selection.size() != 1) {
977
        return;
978
    }
979

980
    // get the needed lists and objects
981
    const std::vector<std::string>& SubNames = selection[0].getSubNames();
982

983
    int GeoIds[2];
984
    Sketcher::PointPos PosIds[2];
985

986
    Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
987

988
    switch (SubNames.size()) {
989
        case 0: {
990
            // Nothing is selected
991
            Gui::TranslatedUserWarning(
992
                Obj,
993
                QObject::tr("Selection is empty"),
994
                QObject::tr("Nothing is selected. Please select end points of curves."));
995
            return;
996
        }
997
        case 1: {
998
            std::vector<int> GeoIdList;
999
            std::vector<Sketcher::PointPos> PosIdList;
1000

1001
            int selGeoId;
1002
            Sketcher::PointPos selPosId;
1003

1004
            getIdsFromName(SubNames[0], Obj, selGeoId, selPosId);
1005

1006
            Obj->getDirectlyCoincidentPoints(selGeoId, selPosId, GeoIdList, PosIdList);
1007

1008
            // Find the right pair of coincident points
1009
            size_t j = 0;
1010
            for (size_t i = 0; i < GeoIdList.size(); ++i) {
1011
                if (Sketcher::PointPos::start == PosIdList[i]
1012
                    || Sketcher::PointPos::end == PosIdList[i]) {
1013
                    if (j < 2) {
1014
                        GeoIds[j] = GeoIdList[i];
1015
                        PosIds[j] = PosIdList[i];
1016
                        ++j;
1017
                    }
1018
                    else {
1019
                        Gui::TranslatedUserWarning(
1020
                            Obj,
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."));
1024

1025
                        return;
1026
                    }
1027
                }
1028
            }
1029
            if (j < 2) {
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."));
1034

1035
                return;
1036
            }
1037

1038
            break;
1039
        }
1040
        case 2: {
1041
            getIdsFromName(SubNames[0], Obj, GeoIds[0], PosIds[0]);
1042
            getIdsFromName(SubNames[1], Obj, GeoIds[1], PosIds[1]);
1043
            break;
1044
        }
1045
        default: {
1046
            Gui::TranslatedUserWarning(
1047
                Obj,
1048
                QObject::tr("Wrong selection"),
1049
                QObject::tr("Two end points, or coincident point should be selected."));
1050

1051
            return;
1052
        }
1053
    }
1054

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;
1065
        }
1066
    }
1067

1068
    Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Join Curves"));
1069
    bool applied = false;
1070

1071
    try {
1072
        Gui::cmdAppObjectArgs(selection[0].getObject(),
1073
                              "join(%d, %d, %d, %d, %d) ",
1074
                              GeoIds[0],
1075
                              static_cast<int>(PosIds[0]),
1076
                              GeoIds[1],
1077
                              static_cast<int>(PosIds[1]),
1078
                              tangentConstraintExists ? 1 : 0);
1079
        applied = true;
1080

1081
        // Warning: GeoId list will have changed
1082
    }
1083
    catch (const Base::Exception& e) {
1084
        Gui::TranslatedUserError(Obj,
1085
                                 QObject::tr("Error"),
1086
                                 QObject::tr(getStrippedPythonExceptionString(e).c_str()));
1087

1088
        getSelection().clearSelection();
1089
    }
1090

1091
    if (applied) {
1092
        Gui::Command::commitCommand();
1093
    }
1094
    else {
1095
        Gui::Command::abortCommand();
1096
    }
1097

1098
    tryAutoRecomputeIfNotSolve(Obj);
1099
    getSelection().clearSelection();
1100
}
1101

1102
bool CmdSketcherJoinCurves::isActive()
1103
{
1104
    return isSketcherBSplineActive(getActiveGuiDocument(), true);
1105
}
1106

1107
void CreateSketcherCommandsBSpline()
1108
{
1109
    Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
1110

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());
1119
}
1120

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.