23
#include "PreCompiled.h"
25
#include <Precision.hxx>
30
#include <boost/range/adaptor/reversed.hpp>
32
#include <App/Application.h>
33
#include <Base/Tools.h>
34
#include <Base/Tools2D.h>
35
#include <Gui/Action.h>
36
#include <Gui/Application.h>
37
#include <Gui/BitmapFactory.h>
38
#include <Gui/CommandT.h>
39
#include <Gui/DlgCheckableMessageBox.h>
40
#include <Gui/Document.h>
41
#include <Gui/MainWindow.h>
42
#include <Gui/Notifications.h>
43
#include <Gui/Selection.h>
44
#include <Gui/SelectionFilter.h>
45
#include <Gui/SelectionObject.h>
46
#include <Mod/Sketcher/App/GeometryFacade.h>
47
#include <Mod/Sketcher/App/SketchObject.h>
48
#include <Mod/Sketcher/App/SolverGeometryExtension.h>
50
#include "CommandConstraints.h"
51
#include "DrawSketchHandler.h"
52
#include "EditDatumDialog.h"
54
#include "ViewProviderSketch.h"
55
#include "ui_InsertDatum.h"
56
#include <Inventor/events/SoKeyboardEvent.h>
61
using namespace SketcherGui;
62
using namespace Sketcher;
67
enum ConstraintCreationMode
74
ConstraintCreationMode constraintCreationMode = Driving;
76
bool isCreateConstraintActive(Gui::Document* doc)
81
&& doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
82
if (static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit())->getSketchMode()
83
== ViewProviderSketch::STATUS_NONE) {
84
if (Gui::Selection().countObjectsOfType(Sketcher::SketchObject::getClassTypeId())
95
void finishDatumConstraint(Gui::Command* cmd,
96
Sketcher::SketchObject* sketch,
97
bool isDriving = true,
98
unsigned int numberofconstraints = 1)
100
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
101
"User parameter:BaseApp/Preferences/Mod/Sketcher");
104
const std::vector<Sketcher::Constraint*>& ConStr = sketch->Constraints.getValues();
105
auto lastConstraintIndex = ConStr.size() - 1;
106
Sketcher::Constraint* constr = ConStr[lastConstraintIndex];
107
auto lastConstraintType = constr->Type;
110
Gui::Document* doc = cmd->getActiveGuiDocument();
111
float scaleFactor = 1.0;
112
double labelPosition = 0.0;
113
float labelPositionRandomness = 0.0;
115
if (lastConstraintType == Radius || lastConstraintType == Diameter) {
116
labelPosition = hGrp->GetFloat("RadiusDiameterConstraintDisplayBaseAngle", 15.0)
118
labelPositionRandomness =
119
hGrp->GetFloat("RadiusDiameterConstraintDisplayAngleRandomness", 0.0)
124
if (labelPositionRandomness != 0.0) {
125
labelPosition = labelPosition
126
+ labelPositionRandomness
127
* (static_cast<float>(std::rand()) / static_cast<float>(RAND_MAX) - 0.5);
131
if (doc && doc->getInEdit()
132
&& doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
133
SketcherGui::ViewProviderSketch* vp =
134
static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
135
scaleFactor = vp->getScaleFactor();
137
int firstConstraintIndex = lastConstraintIndex - numberofconstraints + 1;
139
for (int i = lastConstraintIndex; i >= firstConstraintIndex; i--) {
140
ConStr[i]->LabelDistance = 2. * scaleFactor;
142
if (lastConstraintType == Radius || lastConstraintType == Diameter) {
143
const Part::Geometry* geo = sketch->getGeometry(ConStr[i]->First);
145
if (geo && isCircle(*geo)) {
146
ConStr[i]->LabelPosition = labelPosition;
150
vp->draw(false, false);
153
bool show = hGrp->GetBool("ShowDialogOnDistanceConstraint", true);
156
if (show && isDriving) {
157
EditDatumDialog editDatumDialog(sketch, ConStr.size() - 1);
158
editDatumDialog.exec();
162
cmd->commitCommand();
165
tryAutoRecompute(sketch);
166
cmd->getSelection().clearSelection();
169
void showNoConstraintBetweenExternal(const App::DocumentObject* obj)
171
Gui::TranslatedUserWarning(
173
QObject::tr("Wrong selection"),
174
QObject::tr("Cannot add a constraint between two external geometries."));
177
void showNoConstraintBetweenFixedGeometry(const App::DocumentObject* obj)
179
Gui::TranslatedUserWarning(obj,
180
QObject::tr("Wrong selection"),
181
QObject::tr("Cannot add a constraint between two fixed geometries. "
182
"Fixed geometries include external geometry, "
183
"blocked geometry, and special points "
184
"such as B-spline knot points."));
187
bool isGeoConcentricCompatible(const Part::Geometry* geo)
189
return (isEllipse(*geo) || isArcOfEllipse(*geo) || isCircle(*geo) || isArcOfCircle(*geo));
197
bool removeRedundantPointOnObject(SketchObject* Obj, int GeoId1, int GeoId2, int GeoId3)
199
const std::vector<Constraint*>& cvals = Obj->Constraints.getValues();
201
std::vector<int> cidsToBeRemoved;
204
for (auto it = cvals.begin(); it != cvals.end(); ++it, ++cid) {
205
if ((*it)->Type == Sketcher::PointOnObject &&
206
(((*it)->First == GeoId3 && (*it)->Second == GeoId1) ||
207
((*it)->First == GeoId3 && (*it)->Second == GeoId2))) {
211
const Part::Geometry* geom = Obj->getGeometry((*it)->Second);
212
if (isBSplineCurve(*geom))
213
cidsToBeRemoved.push_back(cid);
217
if (!cidsToBeRemoved.empty()) {
218
for (auto it = cidsToBeRemoved.rbegin(); it != cidsToBeRemoved.rend(); ++it) {
219
Gui::cmdAppObjectArgs(Obj,
227
tryAutoRecomputeIfNotSolve(Obj);
229
notifyConstraintSubstitutions(QObject::tr("One or two point on object constraint(s) was/were deleted, "
230
"since the latest constraint being applied internally applies point-on-object as well."));
241
void SketcherGui::makeAngleBetweenTwoLines(Sketcher::SketchObject* Obj,
246
Sketcher::PointPos posId1 = Sketcher::PointPos::none;
247
Sketcher::PointPos posId2 = Sketcher::PointPos::none;
250
if (!calculateAngle(Obj, geoId1, geoId2, posId1, posId2, actAngle)) {
254
if (actAngle == 0.0) {
255
Gui::TranslatedUserWarning(
257
QObject::tr("Parallel lines"),
258
QObject::tr("An angle constraint cannot be set for two parallel lines."));
263
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add angle constraint"));
264
Gui::cmdAppObjectArgs(Obj,
265
"addConstraint(Sketcher.Constraint('Angle',%d,%d,%d,%d,%f))",
267
static_cast<int>(posId1),
269
static_cast<int>(posId2),
272
if (areBothPointsOrSegmentsFixed(Obj, geoId1, geoId2)
273
|| constraintCreationMode == Reference) {
275
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
277
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
278
finishDatumConstraint(cmd, Obj, false);
281
finishDatumConstraint(cmd, Obj, true);
285
bool SketcherGui::calculateAngle(Sketcher::SketchObject* Obj, int& GeoId1, int& GeoId2, Sketcher::PointPos& PosId1, Sketcher::PointPos& PosId2, double& ActAngle)
287
const Part::Geometry* geom1 = Obj->getGeometry(GeoId1);
288
const Part::Geometry* geom2 = Obj->getGeometry(GeoId2);
290
if (!(geom1->is<Part::GeomLineSegment>()) ||
291
!(geom2->is<Part::GeomLineSegment>())) {
295
const Part::GeomLineSegment* lineSeg1 = static_cast<const Part::GeomLineSegment*>(geom1);
296
const Part::GeomLineSegment* lineSeg2 = static_cast<const Part::GeomLineSegment*>(geom2);
299
Base::Vector3d p1[2], p2[2];
300
p1[0] = lineSeg1->getStartPoint();
301
p1[1] = lineSeg1->getEndPoint();
302
p2[0] = lineSeg2->getStartPoint();
303
p2[1] = lineSeg2->getEndPoint();
306
Base::Line2d line1(Base::Vector2d(p1[0].x, p1[0].y), Base::Vector2d(p1[1].x, p1[1].y));
307
Base::Line2d line2(Base::Vector2d(p2[0].x, p2[0].y), Base::Vector2d(p2[1].x, p2[1].y));
309
if (line1.Intersect(line2, s)) {
311
Base::Vector3d s3d(s.x, s.y, p1[0].z);
312
if (Base::DistanceP2(s3d, p1[0]) < Base::DistanceP2(s3d, p1[1]))
313
PosId1 = Sketcher::PointPos::start;
315
PosId1 = Sketcher::PointPos::end;
316
if (Base::DistanceP2(s3d, p2[0]) < Base::DistanceP2(s3d, p2[1]))
317
PosId2 = Sketcher::PointPos::start;
319
PosId2 = Sketcher::PointPos::end;
323
double length = DBL_MAX;
324
for (int i = 0; i <= 1; i++) {
325
for (int j = 0; j <= 1; j++) {
326
double tmp = Base::DistanceP2(p2[j], p1[i]);
329
PosId1 = i ? Sketcher::PointPos::end : Sketcher::PointPos::start;
330
PosId2 = j ? Sketcher::PointPos::end : Sketcher::PointPos::start;
336
Base::Vector3d dir1 = ((PosId1 == Sketcher::PointPos::start) ? 1. : -1.) *
337
(lineSeg1->getEndPoint() - lineSeg1->getStartPoint());
338
Base::Vector3d dir2 = ((PosId2 == Sketcher::PointPos::start) ? 1. : -1.) *
339
(lineSeg2->getEndPoint() - lineSeg2->getStartPoint());
342
Base::Vector3d dir3 = dir1 % dir2;
343
if (dir3.Length() < Precision::Intersection()) {
344
Base::Vector3d dist = (p1[0] - p2[0]) % dir1;
345
if (dist.Sqr() > Precision::Intersection()) {
351
ActAngle = atan2(dir1.x * dir2.y - dir1.y * dir2.x,
352
dir1.y * dir2.y + dir1.x * dir2.x);
356
std::swap(GeoId1, GeoId2);
357
std::swap(PosId1, PosId2);
371
void SketcherGui::makeTangentToEllipseviaNewPoint(Sketcher::SketchObject* Obj,
372
const Part::GeomEllipse* ellipse,
373
const Part::Geometry* geom2,
378
Base::Vector3d center = ellipse->getCenter();
379
double majord = ellipse->getMajorRadius();
380
double minord = ellipse->getMinorRadius();
381
double phi = atan2(ellipse->getMajorAxisDir().y, ellipse->getMajorAxisDir().x);
383
Base::Vector3d center2;
385
if (isEllipse(*geom2)) {
386
center2 = (static_cast<const Part::GeomEllipse*>(geom2))->getCenter();
388
else if (isArcOfEllipse(*geom2)) {
389
center2 = (static_cast<const Part::GeomArcOfEllipse*>(geom2))->getCenter();
391
else if (isCircle(*geom2)) {
392
center2 = (static_cast<const Part::GeomCircle*>(geom2))->getCenter();
394
else if (isArcOfCircle(*geom2)) {
395
center2 = (static_cast<const Part::GeomArcOfCircle*>(geom2))->getCenter();
398
Base::Vector3d direction = center2 - center;
400
atan2(direction.y, direction.x) - phi;
402
Base::Vector3d PoE = Base::Vector3d(
403
center.x + majord * cos(tapprox) * cos(phi) - minord * sin(tapprox) * sin(phi),
404
center.y + majord * cos(tapprox) * sin(phi) + minord * sin(tapprox) * cos(phi),
409
Gui::cmdAppObjectArgs(Obj, "addGeometry(Part.Point(App.Vector(%f,%f,0)), True)", PoE.x, PoE.y);
410
int GeoIdPoint = Obj->getHighestCurveIndex();
413
Gui::cmdAppObjectArgs(Obj,
414
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
416
static_cast<int>(Sketcher::PointPos::start),
419
Gui::cmdAppObjectArgs(Obj,
420
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
422
static_cast<int>(Sketcher::PointPos::start),
425
Gui::cmdAppObjectArgs(Obj,
426
"addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d))",
430
static_cast<int>(Sketcher::PointPos::start));
432
catch (const Base::Exception& e) {
433
Gui::NotifyUserError(Obj,
434
QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
436
Gui::Command::abortCommand();
438
tryAutoRecompute(Obj);
442
Gui::Command::commitCommand();
443
tryAutoRecompute(Obj);
454
void SketcherGui::makeTangentToArcOfEllipseviaNewPoint(Sketcher::SketchObject* Obj,
455
const Part::GeomArcOfEllipse* aoe,
456
const Part::Geometry* geom2,
461
Base::Vector3d center = aoe->getCenter();
462
double majord = aoe->getMajorRadius();
463
double minord = aoe->getMinorRadius();
464
double phi = atan2(aoe->getMajorAxisDir().y, aoe->getMajorAxisDir().x);
466
Base::Vector3d center2;
468
if (isArcOfEllipse(*geom2)) {
469
center2 = (static_cast<const Part::GeomArcOfEllipse*>(geom2))->getCenter();
471
else if (isCircle(*geom2)) {
472
center2 = (static_cast<const Part::GeomCircle*>(geom2))->getCenter();
474
else if (isArcOfCircle(*geom2)) {
475
center2 = (static_cast<const Part::GeomArcOfCircle*>(geom2))->getCenter();
478
Base::Vector3d direction = center2 - center;
480
atan2(direction.y, direction.x) - phi;
482
Base::Vector3d PoE = Base::Vector3d(
483
center.x + majord * cos(tapprox) * cos(phi) - minord * sin(tapprox) * sin(phi),
484
center.y + majord * cos(tapprox) * sin(phi) + minord * sin(tapprox) * cos(phi),
489
Gui::cmdAppObjectArgs(Obj, "addGeometry(Part.Point(App.Vector(%f,%f,0)), True)", PoE.x, PoE.y);
490
int GeoIdPoint = Obj->getHighestCurveIndex();
493
Gui::cmdAppObjectArgs(Obj,
494
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
496
static_cast<int>(Sketcher::PointPos::start),
499
Gui::cmdAppObjectArgs(Obj,
500
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
502
static_cast<int>(Sketcher::PointPos::start),
505
Gui::cmdAppObjectArgs(Obj,
506
"addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d))",
510
static_cast<int>(Sketcher::PointPos::start));
512
catch (const Base::Exception& e) {
513
Gui::NotifyUserError(Obj,
514
QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
516
Gui::Command::abortCommand();
518
tryAutoRecompute(Obj);
522
Gui::Command::commitCommand();
523
tryAutoRecompute(Obj);
534
void SketcherGui::makeTangentToArcOfHyperbolaviaNewPoint(Sketcher::SketchObject* Obj,
535
const Part::GeomArcOfHyperbola* aoh,
536
const Part::Geometry* geom2,
541
Base::Vector3d center = aoh->getCenter();
542
double majord = aoh->getMajorRadius();
543
double minord = aoh->getMinorRadius();
544
Base::Vector3d dirmaj = aoh->getMajorAxisDir();
545
double phi = atan2(dirmaj.y, dirmaj.x);
546
double df = sqrt(majord * majord + minord * minord);
547
Base::Vector3d focus = center + df * dirmaj;
549
Base::Vector3d center2;
551
if (isArcOfHyperbola(*geom2)) {
552
auto aoh2 = static_cast<const Part::GeomArcOfHyperbola*>(geom2);
553
Base::Vector3d dirmaj2 = aoh2->getMajorAxisDir();
554
double majord2 = aoh2->getMajorRadius();
555
double minord2 = aoh2->getMinorRadius();
556
double df2 = sqrt(majord2 * majord2 + minord2 * minord2);
557
center2 = aoh2->getCenter() + df2 * dirmaj2;
559
else if (isArcOfEllipse(*geom2)) {
560
center2 = (static_cast<const Part::GeomArcOfEllipse*>(geom2))->getCenter();
562
else if (isEllipse(*geom2)) {
563
center2 = (static_cast<const Part::GeomEllipse*>(geom2))->getCenter();
565
else if (isCircle(*geom2)) {
566
center2 = (static_cast<const Part::GeomCircle*>(geom2))->getCenter();
568
else if (isArcOfCircle(*geom2)) {
569
center2 = (static_cast<const Part::GeomArcOfCircle*>(geom2))->getCenter();
571
else if (isLineSegment(*geom2)) {
572
auto l2 = static_cast<const Part::GeomLineSegment*>(geom2);
573
center2 = (l2->getStartPoint() + l2->getEndPoint()) / 2;
576
Base::Vector3d direction = center2 - focus;
577
double tapprox = atan2(direction.y, direction.x) - phi;
579
Base::Vector3d PoH = Base::Vector3d(
580
center.x + majord * cosh(tapprox) * cos(phi) - minord * sinh(tapprox) * sin(phi),
581
center.y + majord * cosh(tapprox) * sin(phi) + minord * sinh(tapprox) * cos(phi),
586
Gui::cmdAppObjectArgs(Obj, "addGeometry(Part.Point(App.Vector(%f,%f,0)), True)", PoH.x, PoH.y);
587
int GeoIdPoint = Obj->getHighestCurveIndex();
590
Gui::cmdAppObjectArgs(Obj,
591
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
593
static_cast<int>(Sketcher::PointPos::start),
596
Gui::cmdAppObjectArgs(Obj,
597
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
599
static_cast<int>(Sketcher::PointPos::start),
602
Gui::cmdAppObjectArgs(Obj,
603
"addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d))",
607
static_cast<int>(Sketcher::PointPos::start));
609
catch (const Base::Exception& e) {
610
Gui::NotifyUserError(Obj,
611
QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
613
Gui::Command::abortCommand();
615
tryAutoRecompute(Obj);
619
Gui::Command::commitCommand();
621
tryAutoRecompute(Obj);
631
void SketcherGui::makeTangentToArcOfParabolaviaNewPoint(Sketcher::SketchObject* Obj,
632
const Part::GeomArcOfParabola* aop,
633
const Part::Geometry* geom2,
638
Base::Vector3d focus = aop->getFocus();
640
Base::Vector3d center2;
642
if (isArcOfParabola(*geom2)) {
643
center2 = (static_cast<const Part::GeomArcOfParabola*>(geom2))->getFocus();
645
else if (isArcOfHyperbola(*geom2)) {
646
auto aoh2 = static_cast<const Part::GeomArcOfHyperbola*>(geom2);
647
Base::Vector3d dirmaj2 = aoh2->getMajorAxisDir();
648
double majord2 = aoh2->getMajorRadius();
649
double minord2 = aoh2->getMinorRadius();
650
double df2 = sqrt(majord2 * majord2 + minord2 * minord2);
651
center2 = aoh2->getCenter() + df2 * dirmaj2;
653
else if (isArcOfEllipse(*geom2)) {
654
center2 = (static_cast<const Part::GeomArcOfEllipse*>(geom2))->getCenter();
656
else if (isEllipse(*geom2)) {
657
center2 = (static_cast<const Part::GeomEllipse*>(geom2))->getCenter();
659
else if (isCircle(*geom2)) {
660
center2 = (static_cast<const Part::GeomCircle*>(geom2))->getCenter();
662
else if (isArcOfCircle(*geom2)) {
663
center2 = (static_cast<const Part::GeomArcOfCircle*>(geom2))->getCenter();
665
else if (isLineSegment(*geom2)) {
666
auto l2 = static_cast<const Part::GeomLineSegment*>(geom2);
667
center2 = (l2->getStartPoint() + l2->getEndPoint()) / 2;
670
Base::Vector3d direction = center2 - focus;
672
Base::Vector3d PoP = focus + direction / 2;
676
Gui::cmdAppObjectArgs(Obj, "addGeometry(Part.Point(App.Vector(%f,%f,0)), True)", PoP.x, PoP.y);
677
int GeoIdPoint = Obj->getHighestCurveIndex();
680
Gui::cmdAppObjectArgs(Obj,
681
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
683
static_cast<int>(Sketcher::PointPos::start),
686
Gui::cmdAppObjectArgs(Obj,
687
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
689
static_cast<int>(Sketcher::PointPos::start),
692
Gui::cmdAppObjectArgs(Obj,
693
"addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d))",
697
static_cast<int>(Sketcher::PointPos::start));
699
catch (const Base::Exception& e) {
700
Gui::NotifyUserError(Obj,
701
QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
704
Gui::Command::abortCommand();
706
tryAutoRecompute(Obj);
710
Gui::Command::commitCommand();
711
tryAutoRecompute(Obj);
714
void SketcherGui::doEndpointTangency(Sketcher::SketchObject* Obj,
721
const Part::Geometry* geom1 = Obj->getGeometry(GeoId1);
722
const Part::Geometry* geom2 = Obj->getGeometry(GeoId2);
724
if (geom1 && geom2 && (isBSplineCurve(*geom1) || isBSplineCurve(*geom2))) {
725
if (! isBSplineCurve(*geom1)) {
726
std::swap(GeoId1, GeoId2);
727
std::swap(PosId1, PosId2);
732
Gui::cmdAppObjectArgs(Obj,
733
"addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d,%d))",
735
static_cast<int>(PosId1),
737
static_cast<int>(PosId2));
740
void SketcherGui::doEndpointToEdgeTangency(Sketcher::SketchObject* Obj,
745
Gui::cmdAppObjectArgs(Obj,
746
"addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d))",
748
static_cast<int>(PosId1),
752
void SketcherGui::notifyConstraintSubstitutions(const QString& message)
754
Gui::Dialog::DlgCheckableMessageBox::showMessage(
755
QObject::tr("Sketcher Constraint Substitution"),
757
QLatin1String("User parameter:BaseApp/Preferences/Mod/Sketcher/General"),
758
QLatin1String("NotifyConstraintSubstitutions"),
761
QObject::tr("Keep notifying me of constraint substitutions"));
765
bool addConstraintSafely(SketchObject* obj, std::function<void()> constraintadditionfunction)
768
constraintadditionfunction();
770
catch (const Base::IndexError& e) {
774
Gui::NotifyUserError(obj,
775
QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
778
Gui::Command::abortCommand();
780
tryAutoRecompute(obj);
783
catch (const Base::Exception&) {
784
Gui::TranslatedUserError(
786
QObject::tr("Error"),
787
QObject::tr("Unexpected error. More information may be available in the Report View."));
789
Gui::Command::abortCommand();
791
tryAutoRecompute(obj);
804
Sketcher::PointPos PosId;
807
struct SketchSelection
817
struct SketchSelectionItem
823
std::list<SketchSelectionItem> Items;
827
int SketchSelection::setUp()
829
std::vector<Gui::SelectionObject> selection = Gui::Selection().getSelectionEx();
831
Sketcher::SketchObject* SketchObj = nullptr;
832
std::vector<std::string> SketchSubNames;
833
std::vector<std::string> SupportSubNames;
836
if (selection.size() == 1) {
838
if (!selection[0].getObject()->isDerivedFrom<Sketcher::SketchObject>()) {
839
ErrorMsg = QObject::tr("Only sketch and its support are allowed to be selected.");
843
SketchSubNames = selection[0].getSubNames();
845
else if (selection.size() == 2) {
846
if (selection[0].getObject()->isDerivedFrom<Sketcher::SketchObject>()) {
847
SketchObj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
849
if (selection[1].getObject() != SketchObj->AttachmentSupport.getValue()) {
850
ErrorMsg = QObject::tr("Only sketch and its support are allowed to be selected.");
854
assert(selection[1].getObject()->isDerivedFrom<Part::Feature>());
855
SketchSubNames = selection[0].getSubNames();
856
SupportSubNames = selection[1].getSubNames();
858
else if (selection[1].getObject()->isDerivedFrom<Sketcher::SketchObject>()) {
859
SketchObj = static_cast<Sketcher::SketchObject*>(selection[1].getObject());
861
if (selection[0].getObject() != SketchObj->AttachmentSupport.getValue()) {
862
ErrorMsg = QObject::tr("Only sketch and its support are allowed to be selected.");
866
assert(selection[0].getObject()->isDerivedFrom<Part::Feature>());
867
SketchSubNames = selection[1].getSubNames();
868
SupportSubNames = selection[0].getSubNames();
871
ErrorMsg = QObject::tr("One of the selected has to be on the sketch.");
895
SelVertexOrRoot = 64,
911
class GenericConstraintSelection: public Gui::SelectionFilterGate
913
App::DocumentObject* object;
916
explicit GenericConstraintSelection(App::DocumentObject* obj)
917
: Gui::SelectionFilterGate(nullPointer())
922
bool allow(App::Document*, App::DocumentObject* pObj, const char* sSubName) override
924
if (pObj != this->object) {
927
if (!sSubName || sSubName[0] == '\0') {
930
std::string element(sSubName);
931
if ((allowedSelTypes & (SelRoot | SelVertexOrRoot) && element.substr(0, 9) == "RootPoint")
932
|| (allowedSelTypes & (SelVertex | SelVertexOrRoot) && element.substr(0, 6) == "Vertex")
933
|| (allowedSelTypes & (SelEdge | SelEdgeOrAxis) && element.substr(0, 4) == "Edge")
934
|| (allowedSelTypes & (SelHAxis | SelEdgeOrAxis) && element.substr(0, 6) == "H_Axis")
935
|| (allowedSelTypes & (SelVAxis | SelEdgeOrAxis) && element.substr(0, 6) == "V_Axis")
936
|| (allowedSelTypes & SelExternalEdge && element.substr(0, 12) == "ExternalEdge")) {
943
void setAllowedSelTypes(unsigned int types)
946
allowedSelTypes = types;
960
class CmdSketcherConstraint: public Gui::Command
962
friend class DrawSketchHandlerGenConstraint;
965
explicit CmdSketcherConstraint(const char* name)
969
~CmdSketcherConstraint() override
972
const char* className() const override
974
return "CmdSketcherConstraint";
992
std::vector<std::vector<SketcherGui::SelType>> allowedSelSequences;
994
virtual void applyConstraint(std::vector<SelIdPair>&, int)
996
void activated(int ) override;
997
bool isActive() override
999
return isCommandActive(getActiveGuiDocument());
1003
class DrawSketchHandlerGenConstraint: public DrawSketchHandler
1006
explicit DrawSketchHandlerGenConstraint(CmdSketcherConstraint* _cmd)
1010
~DrawSketchHandlerGenConstraint() override
1012
Gui::Selection().rmvSelectionGate();
1015
void mouseMove(Base::Vector2d ) override
1018
bool pressButton(Base::Vector2d ) override
1023
bool releaseButton(Base::Vector2d onSketchPos) override
1025
SelIdPair selIdPair;
1026
selIdPair.GeoId = GeoEnum::GeoUndef;
1027
selIdPair.PosId = Sketcher::PointPos::none;
1028
std::stringstream ss;
1029
SelType newSelType = SelUnknown;
1032
int VtId = getPreselectPoint();
1033
int CrvId = getPreselectCurve();
1034
int CrsId = getPreselectCross();
1035
if (allowedSelTypes & (SelRoot | SelVertexOrRoot) && CrsId == 0) {
1036
selIdPair.GeoId = Sketcher::GeoEnum::RtPnt;
1037
selIdPair.PosId = Sketcher::PointPos::start;
1038
newSelType = (allowedSelTypes & SelRoot) ? SelRoot : SelVertexOrRoot;
1041
else if (allowedSelTypes & (SelVertex | SelVertexOrRoot) && VtId >= 0) {
1042
sketchgui->getSketchObject()->getGeoVertexIndex(VtId, selIdPair.GeoId, selIdPair.PosId);
1043
newSelType = (allowedSelTypes & SelVertex) ? SelVertex : SelVertexOrRoot;
1044
ss << "Vertex" << VtId + 1;
1046
else if (allowedSelTypes & (SelEdge | SelEdgeOrAxis) && CrvId >= 0) {
1047
selIdPair.GeoId = CrvId;
1048
newSelType = (allowedSelTypes & SelEdge) ? SelEdge : SelEdgeOrAxis;
1049
ss << "Edge" << CrvId + 1;
1051
else if (allowedSelTypes & (SelHAxis | SelEdgeOrAxis) && CrsId == 1) {
1052
selIdPair.GeoId = Sketcher::GeoEnum::HAxis;
1053
newSelType = (allowedSelTypes & SelHAxis) ? SelHAxis : SelEdgeOrAxis;
1056
else if (allowedSelTypes & (SelVAxis | SelEdgeOrAxis) && CrsId == 2) {
1057
selIdPair.GeoId = Sketcher::GeoEnum::VAxis;
1058
newSelType = (allowedSelTypes & SelVAxis) ? SelVAxis : SelEdgeOrAxis;
1061
else if (allowedSelTypes & SelExternalEdge && CrvId <= Sketcher::GeoEnum::RefExt) {
1063
selIdPair.GeoId = CrvId;
1064
newSelType = SelExternalEdge;
1065
ss << "ExternalEdge" << Sketcher::GeoEnum::RefExt + 1 - CrvId;
1068
if (selIdPair.GeoId == GeoEnum::GeoUndef) {
1071
resetOngoingSequences();
1072
Gui::Selection().clearSelection();
1076
selSeq.push_back(selIdPair);
1077
Gui::Selection().addSelection(sketchgui->getSketchObject()->getDocument()->getName(),
1078
sketchgui->getSketchObject()->getNameInDocument(),
1083
_tempOnSequences.clear();
1084
allowedSelTypes = 0;
1085
for (std::set<int>::iterator token = ongoingSequences.begin();
1086
token != ongoingSequences.end();
1088
if ((cmd->allowedSelSequences).at(*token).at(seqIndex) == newSelType) {
1089
if (seqIndex == (cmd->allowedSelSequences).at(*token).size() - 1) {
1091
cmd->applyConstraint(selSeq, *token);
1094
resetOngoingSequences();
1098
_tempOnSequences.insert(*token);
1100
allowedSelTypes | (cmd->allowedSelSequences).at(*token).at(seqIndex + 1);
1105
std::swap(_tempOnSequences, ongoingSequences);
1107
selFilterGate->setAllowedSelTypes(allowedSelTypes);
1114
void activated() override
1116
selFilterGate = new GenericConstraintSelection(sketchgui->getObject());
1118
resetOngoingSequences();
1122
Gui::Selection().rmvSelectionGate();
1123
Gui::Selection().addSelectionGate(selFilterGate);
1126
qreal pixelRatio = devicePixelRatio();
1127
const unsigned long defaultCrosshairColor = 0xFFFFFF;
1128
unsigned long color = getCrosshairColor();
1129
auto colorMapping = std::map<unsigned long, unsigned long>();
1130
colorMapping[defaultCrosshairColor] = color;
1132
qreal fullIconWidth = 32 * pixelRatio;
1133
qreal iconWidth = 16 * pixelRatio;
1134
QPixmap cursorPixmap =
1135
Gui::BitmapFactory().pixmapFromSvg("Sketcher_Crosshair",
1136
QSizeF(fullIconWidth, fullIconWidth),
1138
icon = Gui::BitmapFactory().pixmapFromSvg(cmd->getPixmap(),
1139
QSizeF(iconWidth, iconWidth));
1140
QPainter cursorPainter;
1141
cursorPainter.begin(&cursorPixmap);
1142
cursorPainter.drawPixmap(16 * pixelRatio, 16 * pixelRatio, icon);
1143
cursorPainter.end();
1146
cursorPixmap.setDevicePixelRatio(pixelRatio);
1148
if (qGuiApp->platformName() == QLatin1String("xcb")) {
1152
setCursor(cursorPixmap, hotX, hotY, false);
1156
CmdSketcherConstraint* cmd;
1158
GenericConstraintSelection* selFilterGate = nullptr;
1160
std::vector<SelIdPair> selSeq;
1161
unsigned int allowedSelTypes = 0;
1164
std::set<int> ongoingSequences, _tempOnSequences;
1166
unsigned int seqIndex;
1168
void resetOngoingSequences()
1170
ongoingSequences.clear();
1171
for (unsigned int i = 0; i < cmd->allowedSelSequences.size(); i++) {
1172
ongoingSequences.insert(i);
1177
allowedSelTypes = 0;
1178
for (std::vector<std::vector<SelType>>::const_iterator it =
1179
cmd->allowedSelSequences.begin();
1180
it != cmd->allowedSelSequences.end();
1182
allowedSelTypes = allowedSelTypes | (*it).at(seqIndex);
1184
selFilterGate->setAllowedSelTypes(allowedSelTypes);
1186
Gui::Selection().clearSelection();
1190
void CmdSketcherConstraint::activated(int )
1192
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
1193
getSelection().clearSelection();
1198
class CmdSketcherCompDimensionTools : public Gui::GroupCommand
1201
CmdSketcherCompDimensionTools()
1202
: GroupCommand("Sketcher_CompDimensionTools")
1204
sAppModule = "Sketcher";
1205
sGroup = "Sketcher";
1206
sMenuText = QT_TR_NOOP("Dimension");
1207
sToolTipText = QT_TR_NOOP("Dimension tools.");
1208
sWhatsThis = "Sketcher_CompDimensionTools";
1209
sStatusTip = sToolTipText;
1212
setCheckable(false);
1213
setRememberLast(false);
1215
addCommand("Sketcher_Dimension");
1217
addCommand("Sketcher_ConstrainDistanceX");
1218
addCommand("Sketcher_ConstrainDistanceY");
1219
addCommand("Sketcher_ConstrainDistance");
1220
addCommand("Sketcher_ConstrainRadiam");
1221
addCommand("Sketcher_ConstrainRadius");
1222
addCommand("Sketcher_ConstrainDiameter");
1223
addCommand("Sketcher_ConstrainAngle");
1224
addCommand("Sketcher_ConstrainLock");
1227
void updateAction(int mode) override
1229
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(getAction());
1234
QList<QAction*> al = pcAction->actions();
1235
int index = pcAction->property("defaultAction").toInt();
1238
al[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Dimension_Driven"));
1240
al[2]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_HorizontalDistance_Driven"));
1241
al[3]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_VerticalDistance_Driven"));
1242
al[4]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Length_Driven"));
1243
al[5]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radiam_Driven"));
1244
al[6]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radius_Driven"));
1245
al[7]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Diameter_Driven"));
1246
al[8]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_InternalAngle_Driven"));
1247
al[9]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Lock_Driven"));
1248
getAction()->setIcon(al[index]->icon());
1251
al[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Dimension"));
1253
al[2]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_HorizontalDistance"));
1254
al[3]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_VerticalDistance"));
1255
al[4]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Length"));
1256
al[5]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radiam"));
1257
al[6]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radius"));
1258
al[7]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Diameter"));
1259
al[8]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_InternalAngle"));
1260
al[9]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Lock"));
1261
getAction()->setIcon(al[index]->icon());
1266
const char* className() const override { return "CmdSketcherCompDimensionTools"; }
1271
class CmdSketcherCompConstrainTools : public Gui::GroupCommand
1274
CmdSketcherCompConstrainTools()
1275
: GroupCommand("Sketcher_CompConstrainTools")
1277
sAppModule = "Sketcher";
1278
sGroup = "Sketcher";
1279
sMenuText = QT_TR_NOOP("Constrain");
1280
sToolTipText = QT_TR_NOOP("Constrain tools.");
1281
sWhatsThis = "Sketcher_CompConstrainTools";
1282
sStatusTip = sToolTipText;
1285
setCheckable(false);
1286
setRememberLast(false);
1288
addCommand("Sketcher_ConstrainCoincidentUnified");
1289
addCommand("Sketcher_ConstrainHorVer");
1290
addCommand("Sketcher_ConstrainParallel");
1291
addCommand("Sketcher_ConstrainPerpendicular");
1292
addCommand("Sketcher_ConstrainTangent");
1293
addCommand("Sketcher_ConstrainEqual");
1294
addCommand("Sketcher_ConstrainSymmetric");
1295
addCommand("Sketcher_ConstrainBlock");
1297
const char* className() const override { return "CmdSketcherCompConstrainTools"; }
1302
class CmdSketcherCompToggleConstraints : public Gui::GroupCommand
1305
CmdSketcherCompToggleConstraints()
1306
: GroupCommand("Sketcher_CompToggleConstraints")
1308
sAppModule = "Sketcher";
1309
sGroup = "Sketcher";
1310
sMenuText = QT_TR_NOOP("Toggle constraints");
1311
sToolTipText = QT_TR_NOOP("Toggle constrain tools.");
1312
sWhatsThis = "Sketcher_CompToggleConstraints";
1313
sStatusTip = sToolTipText;
1316
setCheckable(false);
1317
setRememberLast(false);
1319
addCommand("Sketcher_ToggleDrivingConstraint");
1320
addCommand("Sketcher_ToggleActiveConstraint");
1323
const char* className() const override
1325
return "CmdSketcherCompToggleConstraints";
1327
bool isActive() override
1329
return isCommandActive(getActiveGuiDocument());
1335
class GeomSelectionSizes
1338
GeomSelectionSizes(size_t s_pts, size_t s_lns, size_t s_cir, size_t s_ell, size_t s_spl) :
1339
s_pts(s_pts), s_lns(s_lns), s_cir(s_cir), s_ell(s_ell), s_spl(s_spl) {}
1340
~GeomSelectionSizes() {}
1342
bool hasPoints() const { return s_pts > 0; }
1343
bool hasLines() const { return s_lns > 0; }
1344
bool hasCirclesOrArcs() const { return s_cir > 0; }
1345
bool hasEllipseAndCo() const { return s_ell > 0; }
1346
bool hasSplineAndCo() const { return s_spl > 0; }
1348
bool has1Point() const { return s_pts == 1 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0; }
1349
bool has2Points() const { return s_pts == 2 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0; }
1350
bool has1Point1Line() const { return s_pts == 1 && s_lns == 1 && s_cir == 0 && s_ell == 0 && s_spl == 0; }
1351
bool has3Points() const { return s_pts == 3 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0; }
1352
bool has4MorePoints() const { return s_pts >= 4 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0; }
1353
bool has2Points1Line() const { return s_pts == 2 && s_lns == 1 && s_cir == 0 && s_ell == 0 && s_spl == 0; }
1354
bool has3MorePoints1Line() const { return s_pts >= 3 && s_lns == 1 && s_cir == 0 && s_ell == 0 && s_spl == 0; }
1355
bool has1Point1Circle() const { return s_pts == 1 && s_lns == 0 && s_cir == 1 && s_ell == 0 && s_spl == 0; }
1356
bool has1MorePoint1Ellipse() const { return s_pts >= 1 && s_lns == 0 && s_cir == 0 && s_ell == 1 && s_spl == 0; }
1358
bool has1Line() const { return s_pts == 0 && s_lns == 1 && s_cir == 0 && s_ell == 0 && s_spl == 0; }
1359
bool has2Lines() const { return s_pts == 0 && s_lns == 2 && s_cir == 0 && s_ell == 0 && s_spl == 0; }
1360
bool has3MoreLines() const { return s_pts == 0 && s_lns >= 3 && s_cir == 0 && s_ell == 0 && s_spl == 0; }
1361
bool has1Line1Circle() const { return s_pts == 0 && s_lns == 1 && s_cir == 1 && s_ell == 0 && s_spl == 0; }
1362
bool has1Line2Circles() const { return s_pts == 0 && s_lns == 1 && s_cir == 2 && s_ell == 0 && s_spl == 0; }
1363
bool has1Line1Ellipse() const { return s_pts == 0 && s_lns == 1 && s_cir == 0 && s_ell == 1 && s_spl == 0; }
1365
bool has1Circle() const { return s_pts == 0 && s_lns == 0 && s_cir == 1 && s_ell == 0 && s_spl == 0; }
1366
bool has2Circles() const { return s_pts == 0 && s_lns == 0 && s_cir == 2 && s_ell == 0 && s_spl == 0; }
1367
bool has3MoreCircles() const { return s_pts == 0 && s_lns == 0 && s_cir >= 3 && s_ell == 0 && s_spl == 0; }
1368
bool has1Circle1Ellipse() const { return s_pts == 0 && s_lns == 0 && s_cir == 1 && s_ell == 1 && s_spl == 0; }
1370
bool has1Ellipse() const { return s_pts == 0 && s_lns == 0 && s_cir == 0 && s_ell == 1 && s_spl == 0; }
1371
bool has2MoreEllipses() const { return s_pts == 0 && s_lns == 0 && s_cir == 0 && s_ell >= 2 && s_spl == 0; }
1372
bool has1Point1Spline1MoreEdge() const { return s_pts == 1 && s_spl >= 1 && (s_lns + s_cir + s_ell + s_spl) == 2; }
1374
size_t s_pts, s_lns, s_cir, s_ell, s_spl;
1377
class DrawSketchHandlerDimension : public DrawSketchHandler
1380
explicit DrawSketchHandlerDimension(std::vector<std::string> SubNames)
1381
: specialConstraint(SpecialConstraint::None)
1382
, availableConstraint(AvailableConstraint::FIRST)
1383
, previousOnSketchPos(Base::Vector2d(0.f, 0.f))
1387
, selEllipseAndCo({})
1388
, selSplineAndCo({})
1389
, initialSelection(std::move(SubNames))
1393
~DrawSketchHandlerDimension() override
1397
enum class AvailableConstraint {
1406
enum class SpecialConstraint {
1407
LineOr2PointsDistance,
1412
void activated() override
1414
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Dimension"));
1416
Obj = sketchgui->getSketchObject();
1419
qreal pixelRatio = devicePixelRatio();
1420
const unsigned long defaultCrosshairColor = 0xFFFFFF;
1421
unsigned long color = getCrosshairColor();
1422
auto colorMapping = std::map<unsigned long, unsigned long>();
1423
colorMapping[defaultCrosshairColor] = color;
1425
qreal fullIconWidth = 32 * pixelRatio;
1426
qreal iconWidth = 16 * pixelRatio;
1427
QPixmap cursorPixmap = Gui::BitmapFactory().pixmapFromSvg("Sketcher_Crosshair", QSizeF(fullIconWidth, fullIconWidth), colorMapping),
1428
icon = Gui::BitmapFactory().pixmapFromSvg("Constraint_Dimension", QSizeF(iconWidth, iconWidth));
1429
QPainter cursorPainter;
1430
cursorPainter.begin(&cursorPixmap);
1431
cursorPainter.drawPixmap(16 * pixelRatio, 16 * pixelRatio, icon);
1432
cursorPainter.end();
1435
cursorPixmap.setDevicePixelRatio(pixelRatio);
1437
if (qGuiApp->platformName() == QLatin1String("xcb")) {
1441
setCursor(cursorPixmap, hotX, hotY, false);
1443
handleInitialSelection();
1446
void deactivated() override
1448
Gui::Command::abortCommand();
1450
sketchgui->draw(false, false);
1453
void registerPressedKey(bool pressed, int key) override
1455
if (key == SoKeyboardEvent::M && pressed) {
1456
if (availableConstraint == AvailableConstraint::FIRST) {
1457
availableConstraint = AvailableConstraint::SECOND;
1459
else if (availableConstraint == AvailableConstraint::SECOND) {
1460
availableConstraint = AvailableConstraint::THIRD;
1462
else if (availableConstraint == AvailableConstraint::THIRD) {
1463
availableConstraint = AvailableConstraint::FOURTH;
1465
else if (availableConstraint == AvailableConstraint::FOURTH) {
1466
availableConstraint = AvailableConstraint::FIFTH;
1468
else if (availableConstraint == AvailableConstraint::FIFTH || availableConstraint == AvailableConstraint::RESET) {
1469
availableConstraint = AvailableConstraint::FIRST;
1471
makeAppropriateConstraint(previousOnSketchPos);
1474
DrawSketchHandler::registerPressedKey(pressed, key);
1478
void mouseMove(Base::Vector2d onSketchPos) override
1480
if (hasBeenAborted()) {
1485
previousOnSketchPos = onSketchPos;
1488
if (specialConstraint == SpecialConstraint::LineOr2PointsDistance)
1489
updateDistanceType(onSketchPos);
1492
if (!cstrIndexes.empty()) {
1493
bool oneMoved = false;
1494
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
1495
int lastConstrIndex = static_cast<int>(ConStr.size()) - 1;
1496
for (int index : cstrIndexes) {
1497
if (ConStr[index]->isDimensional()) {
1498
Base::Vector2d pointWhereToMove = onSketchPos;
1500
if (specialConstraint == SpecialConstraint::Block) {
1501
if (index == lastConstrIndex)
1502
pointWhereToMove.y = Obj->getPoint(selPoints[0].GeoId, selPoints[0].PosId).y;
1504
pointWhereToMove.x = Obj->getPoint(selPoints[0].GeoId, selPoints[0].PosId).x;
1506
moveConstraint(index, pointWhereToMove);
1511
sketchgui->draw(false, false);
1515
bool pressButton(Base::Vector2d onSketchPos) override
1517
Q_UNUSED(onSketchPos)
1521
bool releaseButton(Base::Vector2d onSketchPos) override
1523
Q_UNUSED(onSketchPos);
1524
availableConstraint = AvailableConstraint::FIRST;
1525
SelIdPair selIdPair;
1526
selIdPair.GeoId = GeoEnum::GeoUndef;
1527
selIdPair.PosId = Sketcher::PointPos::none;
1528
std::stringstream ss;
1529
Base::Type newselGeoType = Base::Type::badType();
1531
int VtId = getPreselectPoint();
1532
int CrvId = getPreselectCurve();
1533
int CrsId = getPreselectCross();
1536
Obj->getGeoVertexIndex(VtId,
1537
selIdPair.GeoId, selIdPair.PosId);
1538
newselGeoType = Part::GeomPoint::getClassTypeId();
1539
ss << "Vertex" << VtId + 1;
1541
else if (CrsId == 0) {
1542
selIdPair.GeoId = Sketcher::GeoEnum::RtPnt;
1543
selIdPair.PosId = Sketcher::PointPos::start;
1544
newselGeoType = Part::GeomPoint::getClassTypeId();
1547
else if (CrsId == 1) {
1548
selIdPair.GeoId = Sketcher::GeoEnum::HAxis;
1549
newselGeoType = Part::GeomLineSegment::getClassTypeId();
1552
else if (CrsId == 2) {
1553
selIdPair.GeoId = Sketcher::GeoEnum::VAxis;
1554
newselGeoType = Part::GeomLineSegment::getClassTypeId();
1557
else if (CrvId >= 0 || CrvId <= Sketcher::GeoEnum::RefExt) {
1558
selIdPair.GeoId = CrvId;
1559
const Part::Geometry* geo = Obj->getGeometry(CrvId);
1561
newselGeoType = geo->getTypeId();
1564
ss << "Edge" << CrvId + 1;
1567
ss << "ExternalEdge" << Sketcher::GeoEnum::RefExt + 1 - CrvId;
1571
if (selIdPair.GeoId == GeoEnum::GeoUndef) {
1577
std::vector<SelIdPair>& selVector = getSelectionVector(newselGeoType);
1579
if (notSelectedYet(selIdPair)) {
1581
selVector.push_back(selIdPair);
1583
bool selAllowed = makeAppropriateConstraint(onSketchPos);
1587
Gui::Selection().addSelection(Obj->getDocument()->getName(),
1588
Obj->getNameInDocument(),
1589
ss.str().c_str(), onSketchPos.x, onSketchPos.y, 0.f);
1590
sketchgui->draw(false, false);
1593
selVector.pop_back();
1598
selVector.pop_back();
1599
if (!selectionEmpty()) {
1600
makeAppropriateConstraint(onSketchPos);
1603
restartCommand(QT_TRANSLATE_NOOP("Command", "Dimension"));
1606
Gui::Selection().rmvSelection(Obj->getDocument()->getName(),
1607
Obj->getNameInDocument(),
1609
sketchgui->draw(false, false);
1614
void quit() override
1616
if (!cstrIndexes.empty()) {
1619
sketchgui->draw(false, false);
1622
DrawSketchHandler::quit();
1626
SpecialConstraint specialConstraint;
1627
AvailableConstraint availableConstraint;
1629
Base::Vector2d previousOnSketchPos;
1631
std::vector<SelIdPair> selPoints;
1632
std::vector<SelIdPair> selLine;
1633
std::vector<SelIdPair> selCircleArc;
1634
std::vector<SelIdPair> selEllipseAndCo;
1635
std::vector<SelIdPair> selSplineAndCo;
1637
std::vector<std::string> initialSelection;
1639
std::vector<int> cstrIndexes;
1641
Sketcher::SketchObject* Obj;
1643
void clearRefVectors()
1647
selCircleArc.clear();
1648
selEllipseAndCo.clear();
1649
selSplineAndCo.clear();
1652
void handleInitialSelection()
1654
if (initialSelection.size() == 0) {
1658
availableConstraint = AvailableConstraint::FIRST;
1661
for (auto& selElement : initialSelection) {
1662
SelIdPair selIdPair;
1663
getIdsFromName(selElement, Obj, selIdPair.GeoId, selIdPair.PosId);
1665
Base::Type newselGeoType = Base::Type::badType();
1666
if (isEdge(selIdPair.GeoId, selIdPair.PosId)) {
1667
const Part::Geometry* geo = Obj->getGeometry(selIdPair.GeoId);
1668
newselGeoType = geo->getTypeId();
1670
else if (isVertex(selIdPair.GeoId, selIdPair.PosId)) {
1671
newselGeoType = Part::GeomPoint::getClassTypeId();
1674
std::vector<SelIdPair>& selVector = getSelectionVector(newselGeoType);
1677
selVector.push_back(selIdPair);
1681
bool selAllowed = makeAppropriateConstraint(Base::Vector2d(0.,0.));
1688
void finalizeCommand()
1690
if (hasBeenAborted()) {
1696
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
1697
bool show = hGrp->GetBool("ShowDialogOnDistanceConstraint", true);
1698
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
1700
bool commandHandledInEditDatum = false;
1701
for (int index : cstrIndexes | boost::adaptors::reversed) {
1702
if (show && ConStr[index]->isDimensional() && ConStr[index]->isDriving) {
1703
commandHandledInEditDatum = true;
1704
EditDatumDialog editDatumDialog(sketchgui, index);
1705
editDatumDialog.exec();
1706
if (!editDatumDialog.isSuccess()) {
1712
if (!commandHandledInEditDatum)
1713
Gui::Command::commitCommand();
1716
bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true);
1717
if (continuousMode) {
1721
sketchgui->purgeHandler();
1725
std::vector<SelIdPair>& getSelectionVector(Base::Type selGeoType)
1727
if (selGeoType == Part::GeomPoint::getClassTypeId()) {
1730
else if (selGeoType == Part::GeomLineSegment::getClassTypeId()) {
1733
else if (selGeoType == Part::GeomArcOfCircle::getClassTypeId() ||
1734
selGeoType == Part::GeomCircle::getClassTypeId()) {
1735
return selCircleArc;
1737
else if (selGeoType == Part::GeomEllipse::getClassTypeId() ||
1738
selGeoType == Part::GeomArcOfEllipse::getClassTypeId() ||
1739
selGeoType == Part::GeomArcOfHyperbola::getClassTypeId() ||
1740
selGeoType == Part::GeomArcOfParabola::getClassTypeId()) {
1741
return selEllipseAndCo;
1743
else if (selGeoType == Part::GeomBSplineCurve::getClassTypeId()) {
1744
return selSplineAndCo;
1747
static std::vector<SelIdPair> emptyVector;
1751
bool notSelectedYet(const SelIdPair& elem)
1753
auto contains = [&](const std::vector<SelIdPair>& vec, const SelIdPair& elem) {
1754
for (const auto& x : vec)
1756
if (x.GeoId == elem.GeoId && x.PosId == elem.PosId)
1762
return !contains(selPoints, elem)
1763
&& !contains(selLine, elem)
1764
&& !contains(selCircleArc, elem)
1765
&& !contains(selEllipseAndCo, elem);
1768
bool selectionEmpty()
1770
return selPoints.empty() && selLine.empty() && selCircleArc.empty() && selEllipseAndCo.empty();
1773
bool makeAppropriateConstraint(Base::Vector2d onSketchPos) {
1774
bool selAllowed = false;
1776
GeomSelectionSizes selection(selPoints.size(), selLine.size(), selCircleArc.size(), selEllipseAndCo.size(), selSplineAndCo.size());
1778
if (selection.hasPoints()) {
1779
if (selection.has1Point()) { makeCts_1Point(selAllowed, onSketchPos); }
1780
else if (selection.has2Points()) { makeCts_2Point(selAllowed, onSketchPos); }
1781
else if (selection.has1Point1Line()) { makeCts_1Point1Line(selAllowed, onSketchPos); }
1782
else if (selection.has1Point1Spline1MoreEdge()) { makeCts_1Point1Spline1MoreEdge(selAllowed);}
1783
else if (selection.has3Points()) { makeCts_3Point(selAllowed, selection.s_pts); }
1784
else if (selection.has4MorePoints()) { makeCts_4MorePoint(selAllowed, selection.s_pts); }
1785
else if (selection.has2Points1Line()) { makeCts_2Point1Line(selAllowed, onSketchPos, selection.s_pts); }
1786
else if (selection.has3MorePoints1Line()) { makeCts_3MorePoint1Line(selAllowed, onSketchPos, selection.s_pts); }
1787
else if (selection.has1Point1Circle()) { makeCts_1Point1Circle(selAllowed, onSketchPos); }
1788
else if (selection.has1MorePoint1Ellipse()) { makeCts_1MorePoint1Ellipse(selAllowed); }
1790
else if (selection.hasLines()) {
1791
if (selection.has1Line()) { makeCts_1Line(selAllowed, onSketchPos); }
1792
else if (selection.has2Lines()) { makeCts_2Line(selAllowed, onSketchPos); }
1793
else if (selection.has3MoreLines()) { makeCts_3MoreLine(selAllowed, selection.s_lns); }
1794
else if (selection.has1Line1Circle()) { makeCts_1Line1Circle(selAllowed, onSketchPos); }
1795
else if (selection.has1Line2Circles()) { makeCts_1Line2Circle(selAllowed); }
1796
else if (selection.has1Line1Ellipse()) { makeCts_1Line1Ellipse(selAllowed); }
1798
else if (selection.hasCirclesOrArcs()) {
1799
if (selection.has1Circle()) { makeCts_1Circle(selAllowed, onSketchPos); }
1800
else if (selection.has2Circles()) { makeCts_2Circle(selAllowed, onSketchPos); }
1801
else if (selection.has3MoreCircles()) { makeCts_3MoreCircle(selAllowed, selection.s_cir); }
1802
else if (selection.has1Circle1Ellipse()) { makeCts_1Circle1Ellipse(selAllowed); }
1804
else if (selection.hasEllipseAndCo()) {
1805
if (selection.has1Ellipse()) { makeCts_1Ellipse(selAllowed); }
1806
else if (selection.has2MoreEllipses()) { makeCts_2MoreEllipse(selAllowed, selection.s_ell); }
1811
void makeCts_1Point(bool& selAllowed, Base::Vector2d onSketchPos)
1814
if (availableConstraint == AvailableConstraint::FIRST) {
1815
restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Distance to origin' constraint"));
1816
createDistanceConstrain(selPoints[0].GeoId, selPoints[0].PosId, Sketcher::GeoEnum::RtPnt, Sketcher::PointPos::start, onSketchPos);
1819
if (availableConstraint == AvailableConstraint::SECOND) {
1820
restartCommand(QT_TRANSLATE_NOOP("Command", "Add lock constraint"));
1821
specialConstraint = SpecialConstraint::Block;
1822
createDistanceXYConstrain(Sketcher::DistanceX, selPoints[0].GeoId, selPoints[0].PosId, Sketcher::GeoEnum::RtPnt, Sketcher::PointPos::start, onSketchPos);
1823
createDistanceXYConstrain(Sketcher::DistanceY, selPoints[0].GeoId, selPoints[0].PosId, Sketcher::GeoEnum::RtPnt, Sketcher::PointPos::start, onSketchPos);
1824
availableConstraint = AvailableConstraint::RESET;
1828
void makeCts_2Point(bool& selAllowed, Base::Vector2d onSketchPos)
1831
if (availableConstraint == AvailableConstraint::FIRST) {
1832
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance constraint"));
1833
createDistanceConstrain(selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId, onSketchPos);
1836
if (availableConstraint == AvailableConstraint::SECOND) {
1837
restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Horizontal' constraints"));
1838
createHorizontalConstrain(selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId);
1840
if (availableConstraint == AvailableConstraint::THIRD) {
1841
restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Vertical' constraints"));
1842
createVerticalConstrain(selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId);
1843
availableConstraint = AvailableConstraint::RESET;
1847
void makeCts_1Point1Line(bool& selAllowed, Base::Vector2d onSketchPos)
1850
if (availableConstraint == AvailableConstraint::FIRST) {
1851
restartCommand(QT_TRANSLATE_NOOP("Command", "Add point to line Distance constraint"));
1852
createDistanceConstrain(selPoints[0].GeoId, selPoints[0].PosId, selLine[0].GeoId, selLine[0].PosId, onSketchPos);
1855
if (availableConstraint == AvailableConstraint::SECOND) {
1856
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Symmetry constraint"));
1857
createSymmetryConstrain(selLine[0].GeoId, Sketcher::PointPos::start, selLine[0].GeoId, Sketcher::PointPos::end, selPoints[0].GeoId, selPoints[0].PosId);
1858
availableConstraint = AvailableConstraint::RESET;
1862
void makeCts_3Point(bool& selAllowed, size_t s_pts)
1865
if (s_pts > 0 && availableConstraint == AvailableConstraint::FIRST) {
1866
restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Horizontal' constraints"));
1867
for (size_t i = 0; i < s_pts - 1; i++) {
1868
createHorizontalConstrain(selPoints[i].GeoId, selPoints[i].PosId, selPoints[i + 1].GeoId, selPoints[i + 1].PosId);
1872
if (s_pts > 0 && availableConstraint == AvailableConstraint::SECOND) {
1873
restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Vertical' constraints"));
1874
for (size_t i = 0; i < s_pts - 1; i++) {
1875
createVerticalConstrain(selPoints[i].GeoId, selPoints[i].PosId, selPoints[i + 1].GeoId, selPoints[i + 1].PosId);
1878
if (availableConstraint == AvailableConstraint::THIRD) {
1879
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Symmetry constraints"));
1880
createSymmetryConstrain(selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId, selPoints[2].GeoId, selPoints[2].PosId);
1881
availableConstraint = AvailableConstraint::RESET;
1885
void makeCts_1Point1Spline1MoreEdge(bool& )
1888
if (availableConstraint == AvailableConstraint::FIRST) {
1896
void makeCts_4MorePoint(bool& selAllowed, size_t s_pts)
1899
if (s_pts > 0 && availableConstraint == AvailableConstraint::FIRST) {
1900
restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Horizontal' constraints"));
1901
for (size_t i = 0; i < s_pts - 1; i++) {
1902
createHorizontalConstrain(selPoints[i].GeoId, selPoints[i].PosId, selPoints[i + 1].GeoId, selPoints[i + 1].PosId);
1906
if (s_pts > 0 && availableConstraint == AvailableConstraint::SECOND) {
1907
restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Vertical' constraints"));
1908
for (size_t i = 0; i < s_pts - 1; i++) {
1909
createVerticalConstrain(selPoints[i].GeoId, selPoints[i].PosId, selPoints[i + 1].GeoId, selPoints[i + 1].PosId);
1911
availableConstraint = AvailableConstraint::RESET;
1915
void makeCts_2Point1Line(bool& selAllowed, Base::Vector2d onSketchPos, size_t s_pts)
1918
if (availableConstraint == AvailableConstraint::FIRST) {
1919
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Symmetry constraint"));
1920
createSymmetryConstrain(selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId, selLine[0].GeoId, selLine[0].PosId);
1923
if (availableConstraint == AvailableConstraint::SECOND) {
1924
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance constraints"));
1925
for (size_t i = 0; i < s_pts; i++) {
1926
createDistanceConstrain(selPoints[i].GeoId, selPoints[i].PosId, selLine[0].GeoId, selLine[0].PosId, onSketchPos);
1928
availableConstraint = AvailableConstraint::RESET;
1932
void makeCts_3MorePoint1Line(bool& selAllowed, Base::Vector2d onSketchPos, size_t s_pts)
1935
if (availableConstraint == AvailableConstraint::FIRST) {
1936
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance constraints"));
1937
for (size_t i = 0; i < s_pts; i++) {
1938
createDistanceConstrain(selPoints[i].GeoId, selPoints[i].PosId, selLine[0].GeoId, selLine[0].PosId, onSketchPos);
1941
availableConstraint = AvailableConstraint::RESET;
1945
void makeCts_1Point1Circle(bool& selAllowed, Base::Vector2d onSketchPos)
1948
if (availableConstraint == AvailableConstraint::FIRST) {
1949
restartCommand(QT_TRANSLATE_NOOP("Command", "Add length constraint"));
1950
createDistanceConstrain(selPoints[0].GeoId, selPoints[0].PosId, selCircleArc[0].GeoId, selCircleArc[0].PosId, onSketchPos);
1952
availableConstraint = AvailableConstraint::RESET;
1956
void makeCts_1MorePoint1Ellipse(bool& selAllowed)
1958
Q_UNUSED(selAllowed)
1960
if (availableConstraint == AvailableConstraint::FIRST) {
1966
void makeCts_1Line(bool& selAllowed, Base::Vector2d onSketchPos)
1969
if ((selLine[0].GeoId != Sketcher::GeoEnum::VAxis && selLine[0].GeoId != Sketcher::GeoEnum::HAxis)) {
1971
if (availableConstraint == AvailableConstraint::FIRST) {
1972
restartCommand(QT_TRANSLATE_NOOP("Command", "Add length constraint"));
1973
createDistanceConstrain(selLine[0].GeoId, Sketcher::PointPos::start, selLine[0].GeoId, Sketcher::PointPos::end, onSketchPos);
1976
if (availableConstraint == AvailableConstraint::SECOND) {
1977
if (isHorizontalVerticalBlock(selLine[0].GeoId)) {
1979
availableConstraint = AvailableConstraint::RESET;
1982
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Horizontal constraint"));
1983
createHorizontalConstrain(selLine[0].GeoId, Sketcher::PointPos::none, GeoEnum::GeoUndef, Sketcher::PointPos::none);
1986
if (availableConstraint == AvailableConstraint::THIRD) {
1987
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Vertical constraint"));
1988
createVerticalConstrain(selLine[0].GeoId, Sketcher::PointPos::none, GeoEnum::GeoUndef, Sketcher::PointPos::none);
1990
if (availableConstraint == AvailableConstraint::FOURTH) {
1991
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Block constraint"));
1992
createBlockConstrain(selLine[0].GeoId);
1993
availableConstraint = AvailableConstraint::RESET;
2002
void makeCts_2Line(bool& selAllowed, Base::Vector2d onSketchPos)
2005
if (availableConstraint == AvailableConstraint::FIRST) {
2006
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Angle constraint"));
2007
createAngleConstrain(selLine[0].GeoId, selLine[1].GeoId, onSketchPos);
2010
if (availableConstraint == AvailableConstraint::SECOND) {
2011
if (selLine[0].GeoId == Sketcher::GeoEnum::VAxis || selLine[1].GeoId == Sketcher::GeoEnum::VAxis
2012
|| selLine[0].GeoId == Sketcher::GeoEnum::HAxis || selLine[1].GeoId == Sketcher::GeoEnum::HAxis) {
2016
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Equality constraint"));
2017
createEqualityConstrain(selLine[0].GeoId, selLine[1].GeoId);
2019
availableConstraint = AvailableConstraint::RESET;
2023
void makeCts_3MoreLine(bool& selAllowed, size_t s_lns)
2026
if (s_lns > 0 && availableConstraint == AvailableConstraint::FIRST) {
2027
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Equality constraints"));
2028
for (size_t i = 0; i < s_lns - 1; i++) {
2029
createEqualityConstrain(selLine[i].GeoId, selLine[i + 1].GeoId);
2032
availableConstraint = AvailableConstraint::RESET;
2036
void makeCts_1Line1Circle(bool& selAllowed, Base::Vector2d onSketchPos)
2039
if (availableConstraint == AvailableConstraint::FIRST) {
2040
restartCommand(QT_TRANSLATE_NOOP("Command", "Add length constraint"));
2041
createDistanceConstrain(selCircleArc[0].GeoId, selCircleArc[0].PosId, selLine[0].GeoId, selLine[0].PosId, onSketchPos);
2043
availableConstraint = AvailableConstraint::RESET;
2047
void makeCts_1Line2Circle(bool& selAllowed)
2050
if (availableConstraint == AvailableConstraint::FIRST) {
2051
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Symmetry constraints"));
2052
createSymmetryConstrain(selCircleArc[0].GeoId, Sketcher::PointPos::mid, selCircleArc[1].GeoId, Sketcher::PointPos::mid, selLine[0].GeoId, selLine[0].PosId);
2054
availableConstraint = AvailableConstraint::RESET;
2058
void makeCts_1Line1Ellipse(bool& selAllowed)
2060
Q_UNUSED(selAllowed)
2062
if (availableConstraint == AvailableConstraint::FIRST) {
2068
void makeCts_1Circle(bool& selAllowed, Base::Vector2d onSketchPos)
2070
int geoId = selCircleArc[0].GeoId;
2071
bool reverseOrder = isRadiusDoF(geoId);
2074
if (availableConstraint == AvailableConstraint::FIRST) {
2075
restartCommand(QT_TRANSLATE_NOOP("Command", "Add arc angle constraint"));
2076
createArcAngleConstrain(geoId, onSketchPos);
2079
if (availableConstraint == AvailableConstraint::SECOND) {
2080
restartCommand(QT_TRANSLATE_NOOP("Command", "Add arc length constraint"));
2081
createArcLengthConstrain(geoId, onSketchPos);
2083
if (availableConstraint == AvailableConstraint::THIRD) {
2084
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius constraint"));
2085
createRadiusDiameterConstrain(geoId, onSketchPos, true);
2087
if (availableConstraint == AvailableConstraint::FOURTH) {
2088
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius constraint"));
2089
createRadiusDiameterConstrain(geoId, onSketchPos, false);
2090
availableConstraint = AvailableConstraint::RESET;
2094
if (availableConstraint == AvailableConstraint::FIRST) {
2095
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius constraint"));
2096
createRadiusDiameterConstrain(geoId, onSketchPos, true);
2099
if (availableConstraint == AvailableConstraint::SECOND) {
2100
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius constraint"));
2101
createRadiusDiameterConstrain(geoId, onSketchPos, false);
2102
if (!isArcOfCircle(*Obj->getGeometry(geoId))) {
2104
availableConstraint = AvailableConstraint::RESET;
2107
if (availableConstraint == AvailableConstraint::THIRD) {
2108
restartCommand(QT_TRANSLATE_NOOP("Command", "Add arc angle constraint"));
2109
createArcAngleConstrain(geoId, onSketchPos);
2111
if (availableConstraint == AvailableConstraint::FOURTH) {
2112
restartCommand(QT_TRANSLATE_NOOP("Command", "Add arc length constraint"));
2113
createArcLengthConstrain(geoId, onSketchPos);
2114
availableConstraint = AvailableConstraint::RESET;
2120
void makeCts_2Circle(bool& selAllowed, Base::Vector2d onSketchPos)
2123
if (availableConstraint == AvailableConstraint::FIRST) {
2124
restartCommand(QT_TRANSLATE_NOOP("Command", "Add length constraint"));
2125
createDistanceConstrain(selCircleArc[0].GeoId, selCircleArc[0].PosId, selCircleArc[1].GeoId, selCircleArc[1].PosId, onSketchPos);
2128
if (availableConstraint == AvailableConstraint::SECOND) {
2129
restartCommand(QT_TRANSLATE_NOOP("Command", "Add concentric and length constraint"));
2130
bool created = createCoincidenceConstrain(selCircleArc[0].GeoId, Sketcher::PointPos::mid, selCircleArc[1].GeoId, Sketcher::PointPos::mid);
2132
availableConstraint = AvailableConstraint::THIRD;
2135
createDistanceConstrain(selCircleArc[0].GeoId, selCircleArc[0].PosId, selCircleArc[1].GeoId, selCircleArc[1].PosId, onSketchPos);
2138
if (availableConstraint == AvailableConstraint::THIRD) {
2139
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Equality constraint"));
2140
createEqualityConstrain(selCircleArc[0].GeoId, selCircleArc[1].GeoId);
2141
availableConstraint = AvailableConstraint::RESET;
2145
void makeCts_3MoreCircle(bool& selAllowed, size_t s_cir)
2148
if (s_cir > 0 && availableConstraint == AvailableConstraint::FIRST) {
2149
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Equality constraint"));
2150
for (size_t i = 0; i < s_cir - 1; i++) {
2151
createEqualityConstrain(selCircleArc[i].GeoId, selCircleArc[i + 1].GeoId);
2154
availableConstraint = AvailableConstraint::RESET;
2158
void makeCts_1Circle1Ellipse(bool& selAllowed)
2160
Q_UNUSED(selAllowed)
2162
if (availableConstraint == AvailableConstraint::FIRST) {
2168
void makeCts_1Ellipse(bool& selAllowed)
2174
void makeCts_2MoreEllipse(bool& selAllowed, size_t s_ell)
2177
bool allTheSame = 1;
2178
const Part::Geometry* geom = Obj->getGeometry(selEllipseAndCo[0].GeoId);
2179
Base::Type typeOf = geom->getTypeId();
2180
for (size_t i = 1; i < s_ell; i++) {
2181
const Part::Geometry* geomi = Obj->getGeometry(selEllipseAndCo[i].GeoId);
2182
if (typeOf != geomi->getTypeId()) {
2187
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Equality constraint"));
2188
for (size_t i = 1; i < s_ell; i++) {
2189
createEqualityConstrain(selEllipseAndCo[0].GeoId, selEllipseAndCo[i].GeoId);
2195
void createDistanceConstrain(int GeoId1, Sketcher::PointPos PosId1, int GeoId2, Sketcher::PointPos PosId2, Base::Vector2d onSketchPos) {
2198
if (GeoId1 == GeoId2 || (PosId1 != Sketcher::PointPos::none && PosId2 != Sketcher::PointPos::none)) {
2199
specialConstraint = SpecialConstraint::LineOr2PointsDistance;
2203
if (PosId1 != Sketcher::PointPos::none && PosId2 == Sketcher::PointPos::none) {
2204
Base::Vector3d pnt = Obj->getPoint(GeoId1, PosId1);
2205
double ActDist = 0.;
2206
const Part::Geometry* geom = Obj->getGeometry(GeoId2);
2208
if (isLineSegment(*geom)) {
2209
auto lineSeg = static_cast<const Part::GeomLineSegment*>(geom);
2210
Base::Vector3d pnt1 = lineSeg->getStartPoint();
2211
Base::Vector3d pnt2 = lineSeg->getEndPoint();
2212
Base::Vector3d d = pnt2 - pnt1;
2213
ActDist = std::abs(-pnt.x * d.y + pnt.y * d.x + pnt1.x * pnt2.y - pnt2.x * pnt1.y) / d.Length();
2215
else if (isCircle(*geom)) {
2216
auto circle = static_cast<const Part::GeomCircle*>(geom);
2217
Base::Vector3d ct = circle->getCenter();
2218
Base::Vector3d di = ct - pnt;
2219
ActDist = std::abs(di.Length() - circle->getRadius());
2221
else if (isArcOfCircle(*geom)) {
2222
auto arc = static_cast<const Part::GeomArcOfCircle*>(geom);
2223
Base::Vector3d ct = arc->getCenter();
2224
Base::Vector3d di = ct - pnt;
2225
ActDist = std::abs(di.Length() - arc->getRadius());
2228
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%f)) ",
2229
GeoId1, static_cast<int>(PosId1), GeoId2, ActDist);
2232
else if (PosId1 == Sketcher::PointPos::none && PosId2 == Sketcher::PointPos::none) {
2233
const Part::Geometry* geo1 = Obj->getGeometry(GeoId1);
2234
const Part::Geometry* geo2 = Obj->getGeometry(GeoId2);
2237
Base::Vector3d center1, center2;
2238
if (isCircle(*geo1)) {
2239
auto conic = static_cast<const Part::GeomCircle*>(geo1);
2240
radius1 = conic->getRadius();
2241
center1 = conic->getCenter();
2243
else if (isArcOfCircle(*geo1)) {
2244
auto conic = static_cast<const Part::GeomArcOfCircle*>(geo1);
2245
radius1 = conic->getRadius();
2246
center1 = conic->getCenter();
2248
if (isCircle(*geo2)) {
2249
auto conic = static_cast<const Part::GeomCircle*>(geo2);
2250
radius2 = conic->getRadius();
2251
center2 = conic->getCenter();
2253
else if (isArcOfCircle(*geo2)){
2254
auto conic = static_cast<const Part::GeomArcOfCircle*>(geo2);
2255
radius2 = conic->getRadius();
2256
center2 = conic->getCenter();
2259
if ((isCircle(*geo1) || isArcOfCircle(*geo1)) && isLineSegment(*geo2)) {
2260
auto lineSeg = static_cast<const Part::GeomLineSegment*>(geo2);
2261
Base::Vector3d pnt1 = lineSeg->getStartPoint();
2262
Base::Vector3d pnt2 = lineSeg->getEndPoint();
2263
Base::Vector3d d = pnt2 - pnt1;
2265
std::abs(-center1.x * d.y + center1.y * d.x + pnt1.x * pnt2.y - pnt2.x * pnt1.y)
2269
Gui::cmdAppObjectArgs(Obj,
2270
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%f))",
2276
else if ((isCircle(*geo1) || isArcOfCircle(*geo1))
2277
&& (isCircle(*geo2) || isArcOfCircle(*geo2))) {
2278
double ActDist = 0.;
2280
Base::Vector3d intercenter = center1 - center2;
2281
double intercenterdistance = intercenter.Length();
2283
if (intercenterdistance >= radius1 && intercenterdistance >= radius2) {
2285
ActDist = intercenterdistance - radius1 - radius2;
2288
double bigradius = std::max(radius1, radius2);
2289
double smallradius = std::min(radius1, radius2);
2291
ActDist = bigradius - smallradius - intercenterdistance;
2294
Gui::cmdAppObjectArgs(Obj,
2295
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%f))",
2302
Base::Vector3d pnt1 = Obj->getPoint(GeoId1, PosId1);
2303
Base::Vector3d pnt2 = Obj->getPoint(GeoId2, PosId2);
2305
Gui::cmdAppObjectArgs(Obj,
2306
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%d,%f)) ",
2308
static_cast<int>(PosId1),
2310
static_cast<int>(PosId2),
2311
(pnt2 - pnt1).Length());
2314
finishDimensionCreation(GeoId1, GeoId2, onSketchPos);
2317
void createDistanceXYConstrain(Sketcher::ConstraintType type, int GeoId1, Sketcher::PointPos PosId1, int GeoId2, Sketcher::PointPos PosId2, Base::Vector2d onSketchPos) {
2318
Base::Vector3d pnt1 = Obj->getPoint(GeoId1, PosId1);
2319
Base::Vector3d pnt2 = Obj->getPoint(GeoId2, PosId2);
2320
double ActLength = pnt2.x - pnt1.x;
2322
if (type == Sketcher::DistanceY) {
2323
ActLength = pnt2.y - pnt1.y;
2327
if (ActLength < -Precision::Confusion()) {
2328
std::swap(GeoId1, GeoId2);
2329
std::swap(PosId1, PosId2);
2330
std::swap(pnt1, pnt2);
2331
ActLength = -ActLength;
2334
if (type == Sketcher::DistanceY) {
2335
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%d,%d,%f)) ",
2336
GeoId1, static_cast<int>(PosId1), GeoId2, static_cast<int>(PosId2), ActLength);
2339
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%d,%d,%f)) ",
2340
GeoId1, static_cast<int>(PosId1), GeoId2, static_cast<int>(PosId2), ActLength);
2343
finishDimensionCreation(GeoId1, GeoId2, onSketchPos);
2346
void createRadiusDiameterConstrain(int GeoId, Base::Vector2d onSketchPos, bool firstCstr) {
2347
double radius = 0.0;
2348
bool isCircleGeom = true;
2350
const Part::Geometry* geom = Obj->getGeometry(GeoId);
2355
if (geom && isArcOfCircle(*geom)) {
2356
auto arc = static_cast<const Part::GeomArcOfCircle*>(geom);
2357
radius = arc->getRadius();
2358
isCircleGeom = false;
2360
else if (geom && isCircle(*geom)) {
2361
auto circle = static_cast<const Part::GeomCircle*>(geom);
2362
radius = circle->getRadius();
2365
if (isBsplinePole(geom)) {
2366
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Weight',%d,%f)) ",
2370
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/dimensioning");
2371
bool dimensioningDiameter = hGrp->GetBool("DimensioningDiameter", true);
2372
bool dimensioningRadius = hGrp->GetBool("DimensioningRadius", true);
2374
if ((firstCstr && dimensioningRadius && !dimensioningDiameter) ||
2375
(!firstCstr && !dimensioningRadius && dimensioningDiameter) ||
2376
(firstCstr && dimensioningRadius && dimensioningDiameter && !isCircleGeom) ||
2377
(!firstCstr && dimensioningRadius && dimensioningDiameter && isCircleGeom) ) {
2378
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Radius',%d,%f)) ",
2382
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Diameter',%d,%f)) ",
2387
finishDimensionCreation(GeoId, GeoEnum::GeoUndef, onSketchPos);
2390
bool createCoincidenceConstrain(int GeoId1, Sketcher::PointPos PosId1, int GeoId2, Sketcher::PointPos PosId2) {
2392
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
2397
bool constraintExists = Obj->arePointsCoincident(GeoId1, PosId1, GeoId2, PosId2);
2398
if (!constraintExists && (GeoId1 != GeoId2)) {
2399
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Coincident', %d, %d, %d, %d)) ",
2400
GeoId1, static_cast<int>(PosId1), GeoId2, static_cast<int>(PosId2));
2402
addConstraintIndex();
2408
void createEqualityConstrain(int GeoId1, int GeoId2) {
2410
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
2414
const Part::Geometry* geo1 = Obj->getGeometry(GeoId1);
2415
const Part::Geometry* geo2 = Obj->getGeometry(GeoId2);
2417
if ((isLineSegment(*geo1) && ! isLineSegment(*geo2))
2418
|| (isArcOfHyperbola(*geo1) && ! isArcOfHyperbola(*geo2))
2419
|| (isArcOfParabola(*geo1) && ! isArcOfParabola(*geo2))
2420
|| (isBsplinePole(geo1) && !isBsplinePole(geo2))
2421
|| ((isCircle(*geo1) || isArcOfCircle(*geo1)) && !(isCircle(*geo2) || isArcOfCircle(*geo2)))
2422
|| ((isEllipse(*geo1) || isArcOfEllipse(*geo1)) && !(isEllipse(*geo2) || isArcOfEllipse(*geo2)))) {
2424
Gui::TranslatedUserWarning(Obj,
2425
QObject::tr("Wrong selection"),
2426
QObject::tr("Select two or more edges of similar type."));
2430
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Equal',%d,%d)) ",
2432
addConstraintIndex();
2435
void createAngleConstrain(int GeoId1, int GeoId2, Base::Vector2d onSketchPos) {
2436
Sketcher::PointPos PosId1 = Sketcher::PointPos::none;
2437
Sketcher::PointPos PosId2 = Sketcher::PointPos::none;
2440
if (!calculateAngle(Obj, GeoId1, GeoId2, PosId1, PosId2, ActAngle)) {
2444
if (ActAngle == 0.0) {
2446
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance constraint"));
2447
createDistanceConstrain(selLine[1].GeoId, Sketcher::PointPos::start, selLine[0].GeoId, selLine[0].PosId, onSketchPos);
2451
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Angle',%d,%d,%d,%d,%f)) ",
2452
GeoId1, static_cast<int>(PosId1), GeoId2, static_cast<int>(PosId2), ActAngle);
2454
finishDimensionCreation(GeoId1, GeoId2, onSketchPos);
2457
void createArcLengthConstrain(int GeoId, Base::Vector2d onSketchPos) {
2458
const Part::Geometry* geom = Obj->getGeometry(GeoId);
2459
if (!isArcOfCircle(*geom)) {
2463
const auto* arc = static_cast<const Part::GeomArcOfCircle*>(geom);
2464
double ActLength = arc->getAngle(false) * arc->getRadius();
2466
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Distance',%d,%f))",
2469
finishDimensionCreation(GeoId, GeoEnum::GeoUndef, onSketchPos);
2472
void createArcAngleConstrain(int GeoId, Base::Vector2d onSketchPos) {
2473
const Part::Geometry* geom = Obj->getGeometry(GeoId);
2474
if (!isArcOfCircle(*geom)) {
2478
const auto* arc = static_cast<const Part::GeomArcOfCircle*>(geom);
2479
double angle = arc->getAngle(true);
2481
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Angle',%d,%f))",
2484
finishDimensionCreation(GeoId, GeoEnum::GeoUndef, onSketchPos);
2487
void createVerticalConstrain(int GeoId1, Sketcher::PointPos PosId1, int GeoId2, Sketcher::PointPos PosId2) {
2488
if (selLine.size() == 1) {
2489
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Vertical',%d)) ", GeoId1);
2492
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
2495
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Vertical',%d,%d,%d,%d)) "
2496
, GeoId1, static_cast<int>(PosId1), GeoId2, static_cast<int>(PosId2));
2498
addConstraintIndex();
2499
tryAutoRecompute(Obj);
2501
void createHorizontalConstrain(int GeoId1, Sketcher::PointPos PosId1, int GeoId2, Sketcher::PointPos PosId2) {
2502
if (selLine.size() == 1) {
2503
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Horizontal',%d)) ", GeoId1);
2506
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
2509
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Horizontal',%d,%d,%d,%d)) "
2510
, GeoId1, static_cast<int>(PosId1), GeoId2, static_cast<int>(PosId2));
2512
addConstraintIndex();
2513
tryAutoRecompute(Obj);
2516
void createBlockConstrain(int GeoId) {
2517
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Block',%d)) ", GeoId);
2519
addConstraintIndex();
2520
tryAutoRecompute(Obj);
2523
bool isHorizontalVerticalBlock(int GeoId) {
2524
const std::vector< Sketcher::Constraint* >& vals = Obj->Constraints.getValues();
2527
for (const auto& constraint : vals) {
2528
if ((constraint->Type == Sketcher::Horizontal || constraint->Type == Sketcher::Vertical || constraint->Type == Sketcher::Block)
2529
&& constraint->First == GeoId) {
2536
void createSymmetryConstrain(int GeoId1, Sketcher::PointPos PosId1, int GeoId2, Sketcher::PointPos PosId2, int GeoId3, Sketcher::PointPos PosId3) {
2537
if (selPoints.size() == 2 && selLine.size() == 1) {
2538
if (isEdge(GeoId1, PosId1) && isVertex(GeoId3, PosId3)) {
2539
std::swap(GeoId1, GeoId3);
2540
std::swap(PosId1, PosId3);
2542
else if (isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
2543
std::swap(GeoId2, GeoId3);
2544
std::swap(PosId2, PosId3);
2547
if (areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3)) {
2551
const Part::Geometry* geom = Obj->getGeometry(GeoId3);
2553
if (isLineSegment(*geom)) {
2554
if (GeoId1 == GeoId2 && GeoId2 == GeoId3) {
2555
Gui::TranslatedUserWarning(Obj,
2556
QObject::tr("Wrong selection"),
2557
QObject::tr("Cannot add a symmetry constraint between a line and its end points!"));
2561
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d)) ",
2562
GeoId1, static_cast<int>(PosId1), GeoId2, static_cast<int>(PosId2), GeoId3);
2564
addConstraintIndex();
2565
tryAutoRecompute(Obj);
2569
if (selPoints.size() == 1 && selLine.size() == 1) {
2570
if (GeoId1 == GeoId3) {
2571
Gui::TranslatedUserWarning(Obj,
2572
QObject::tr("Wrong selection"),
2573
QObject::tr("Cannot add a symmetry constraint between a line and its end points!"));
2576
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
2581
if (areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3)) {
2585
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d,%d)) ",
2586
GeoId1, static_cast<int>(PosId1), GeoId2, static_cast<int>(PosId2), GeoId3, static_cast<int>(PosId3));
2588
addConstraintIndex();
2589
tryAutoRecompute(Obj);
2593
void updateDistanceType(Base::Vector2d onSketchPos)
2595
const std::vector< Sketcher::Constraint* >& vals = Obj->Constraints.getValues();
2596
Sketcher::ConstraintType type = vals[vals.size() - 1]->Type;
2598
Base::Vector3d pnt1, pnt2;
2599
bool addedOrigin = false;
2600
if (selPoints.size() == 1) {
2603
SelIdPair selIdPair;
2604
selIdPair.GeoId = Sketcher::GeoEnum::RtPnt;
2605
selIdPair.PosId = Sketcher::PointPos::start;
2606
selPoints.push_back(selIdPair);
2609
if (selLine.size() == 1) {
2610
pnt1 = Obj->getPoint(selLine[0].GeoId, Sketcher::PointPos::start);
2611
pnt2 = Obj->getPoint(selLine[0].GeoId, Sketcher::PointPos::end);
2614
pnt1 = Obj->getPoint(selPoints[0].GeoId, selPoints[0].PosId);
2615
pnt2 = Obj->getPoint(selPoints[1].GeoId, selPoints[1].PosId);
2618
double minX, minY, maxX, maxY;
2619
minX = min(pnt1.x, pnt2.x);
2620
maxX = max(pnt1.x, pnt2.x);
2621
minY = min(pnt1.y, pnt2.y);
2622
maxY = max(pnt1.y, pnt2.y);
2623
if (onSketchPos.x > minX && onSketchPos.x < maxX
2624
&& (onSketchPos.y < minY || onSketchPos.y > maxY) && type != Sketcher::DistanceX) {
2625
restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX constraint"));
2626
specialConstraint = SpecialConstraint::LineOr2PointsDistance;
2627
if (selLine.size() == 1) {
2628
createDistanceXYConstrain(Sketcher::DistanceX, selLine[0].GeoId, Sketcher::PointPos::start, selLine[0].GeoId, Sketcher::PointPos::end, onSketchPos);
2631
createDistanceXYConstrain(Sketcher::DistanceX, selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId, onSketchPos);
2634
else if (onSketchPos.y > minY && onSketchPos.y < maxY
2635
&& (onSketchPos.x < minX || onSketchPos.x > maxX) && type != Sketcher::DistanceY) {
2636
restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceY constraint"));
2637
specialConstraint = SpecialConstraint::LineOr2PointsDistance;
2638
if (selLine.size() == 1) {
2639
createDistanceXYConstrain(Sketcher::DistanceY, selLine[0].GeoId, Sketcher::PointPos::start, selLine[0].GeoId, Sketcher::PointPos::end, onSketchPos);
2642
createDistanceXYConstrain(Sketcher::DistanceY, selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId, onSketchPos);
2645
else if ((((onSketchPos.y < minY || onSketchPos.y > maxY) && (onSketchPos.x < minX || onSketchPos.x > maxX))
2646
|| (onSketchPos.y > minY && onSketchPos.y < maxY && onSketchPos.x > minX && onSketchPos.x < maxX)) && type != Sketcher::Distance) {
2647
restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance constraint"));
2648
if (selLine.size() == 1) {
2649
createDistanceConstrain(selLine[0].GeoId, Sketcher::PointPos::start, selLine[0].GeoId, Sketcher::PointPos::end, onSketchPos);
2652
createDistanceConstrain(selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId, onSketchPos);
2658
selPoints.pop_back();
2662
bool isRadiusDoF(int geoId)
2664
const Part::Geometry* geo = Obj->getGeometry(geoId);
2665
if (!isArcOfCircle(*geo)) {
2670
Gui::Command::abortCommand();
2673
auto solvext = Obj->getSolvedSketch().getSolverExtension(geoId);
2676
auto arcInfo = solvext->getArc();
2678
return !arcInfo.isRadiusDoF();
2684
void finishDimensionCreation(int GeoId1, int GeoId2, Base::Vector2d onSketchPos)
2686
bool fixed = GeoId2 == GeoEnum::GeoUndef ? isPointOrSegmentFixed(Obj, GeoId1) : areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2);
2688
int index = Obj->Constraints.getValues().size() - 1;
2689
if (fixed || constraintCreationMode == Reference) {
2690
Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)", index, "False");
2693
addConstraintIndex();
2694
moveConstraint(index, onSketchPos);
2697
void addConstraintIndex()
2699
cstrIndexes.push_back(Obj->Constraints.getValues().size() - 1);
2702
bool hasBeenAborted()
2705
if (!cstrIndexes.empty()) {
2706
int lastConstrIndex = Obj->Constraints.getSize() - 1;
2707
if (cstrIndexes.back() != lastConstrIndex) {
2715
void restartCommand(const char* cstrName) {
2716
specialConstraint = SpecialConstraint::None;
2717
Gui::Command::abortCommand();
2719
sketchgui->draw(false, false);
2720
Gui::Command::openCommand(cstrName);
2722
cstrIndexes.clear();
2727
Gui::Command::abortCommand();
2728
Gui::Selection().clearSelection();
2729
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Dimension"));
2730
cstrIndexes.clear();
2731
specialConstraint = SpecialConstraint::None;
2732
previousOnSketchPos = Base::Vector2d(0.f, 0.f);
2737
DEF_STD_CMD_AU(CmdSketcherDimension)
2739
CmdSketcherDimension::CmdSketcherDimension()
2740
: Command("Sketcher_Dimension")
2742
sAppModule = "Sketcher";
2743
sGroup = "Sketcher";
2744
sMenuText = QT_TR_NOOP("Dimension");
2745
sToolTipText = QT_TR_NOOP("Constrain contextually based on your selection.\n"
2746
"Depending on your selection you might have several constraints available. You can cycle through them using M key.\n"
2747
"Left clicking on empty space will validate the current constraint. Right clicking or pressing Esc will cancel.");
2748
sWhatsThis = "Sketcher_Dimension";
2749
sStatusTip = sToolTipText;
2750
sPixmap = "Constraint_Dimension";
2755
void CmdSketcherDimension::activated(int iMsg)
2758
App::AutoTransaction::setEnable(false);
2761
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
2762
std::vector<std::string> SubNames = {};
2765
if (selection.size() == 1 && selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
2766
SubNames = selection[0].getSubNames();
2769
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerDimension>(SubNames));
2772
void CmdSketcherDimension::updateAction(int mode)
2777
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Dimension_Driven"));
2781
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Dimension"));
2786
bool CmdSketcherDimension::isActive(void)
2788
return isCommandActive(getActiveGuiDocument());
2793
class CmdSketcherCompHorizontalVertical : public Gui::GroupCommand
2796
CmdSketcherCompHorizontalVertical()
2797
: GroupCommand("Sketcher_CompHorVer")
2799
sAppModule = "Sketcher";
2800
sGroup = "Sketcher";
2801
sMenuText = QT_TR_NOOP("Constrain horizontal/vertical");
2802
sToolTipText = QT_TR_NOOP("Constrains a single line to either horizontal or vertical.");
2803
sWhatsThis = "Sketcher_CompHorVer";
2804
sStatusTip = sToolTipText;
2807
setCheckable(false);
2808
setRememberLast(false);
2810
addCommand("Sketcher_ConstrainHorVer");
2811
addCommand("Sketcher_ConstrainHorizontal");
2812
addCommand("Sketcher_ConstrainVertical");
2815
const char* className() const override
2817
return "CmdSketcherCompHorizontalVertical";
2820
bool isActive() override
2822
return isCommandActive(getActiveGuiDocument());
2827
bool canHorVerBlock(Sketcher::SketchObject* Obj, int geoId)
2829
const std::vector<Sketcher::Constraint*>& vals = Obj->Constraints.getValues();
2832
for (auto& constr : vals) {
2833
if (constr->Type == Sketcher::Horizontal && constr->First == geoId
2834
&& constr->FirstPos == Sketcher::PointPos::none) {
2835
Gui::TranslatedUserWarning(
2837
QObject::tr("Double constraint"),
2838
QObject::tr("The selected edge already has a horizontal constraint!"));
2841
if (constr->Type == Sketcher::Vertical && constr->First == geoId
2842
&& constr->FirstPos == Sketcher::PointPos::none) {
2843
Gui::TranslatedUserWarning(
2845
QObject::tr("Impossible constraint"),
2846
QObject::tr("The selected edge already has a vertical constraint!"));
2850
if (constr->Type == Sketcher::Block && constr->First == geoId
2851
&& constr->FirstPos == Sketcher::PointPos::none) {
2852
Gui::TranslatedUserWarning(
2854
QObject::tr("Impossible constraint"),
2855
QObject::tr("The selected edge already has a Block constraint!"));
2862
void horVerActivated(CmdSketcherConstraint* cmd, std::string type)
2865
std::vector<Gui::SelectionObject> selection = Gui::Command::getSelection().getSelectionEx();
2868
if (selection.size() != 1
2869
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
2870
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
2871
"User parameter:BaseApp/Preferences/Mod/Sketcher");
2872
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
2874
if (constraintMode) {
2875
ActivateHandler(cmd->getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(cmd));
2876
Gui::Command::getSelection().clearSelection();
2879
Gui::TranslatedUserWarning(cmd->getActiveGuiDocument(),
2880
QObject::tr("Wrong selection"),
2881
QObject::tr("Select an edge from the sketch."));
2887
const std::vector<std::string>& SubNames = selection[0].getSubNames();
2888
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
2890
std::vector<int> edgegeoids;
2891
std::vector<int> pointgeoids;
2892
std::vector<Sketcher::PointPos> pointpos;
2894
int fixedpoints = 0;
2896
for (auto& name : SubNames) {
2898
Sketcher::PointPos PosId;
2899
getIdsFromName(name, Obj, GeoId, PosId);
2901
if (isEdge(GeoId, PosId)) {
2902
const Part::Geometry* geo = Obj->getGeometry(GeoId);
2904
if (!isLineSegment(*geo)) {
2905
Gui::TranslatedUserWarning(Obj,
2906
QObject::tr("Impossible constraint"),
2907
QObject::tr("The selected edge is not a line segment."));
2911
if (canHorVerBlock(Obj, GeoId)) {
2912
edgegeoids.push_back(GeoId);
2915
else if (isVertex(GeoId, PosId)) {
2918
if (isPointOrSegmentFixed(Obj, GeoId)) {
2922
pointgeoids.push_back(GeoId);
2923
pointpos.push_back(PosId);
2927
if (edgegeoids.empty() && pointgeoids.size() < 2) {
2928
Gui::TranslatedUserWarning(
2930
QObject::tr("Impossible constraint"),
2931
QObject::tr("The selected item(s) can't accept a horizontal or vertical constraint!"));
2936
if (!edgegeoids.empty()) {
2938
const char* cmdName = type == "Horizontal" ? "Add horizontal constraint" : type == "Vertical" ? "Add vertical constraint" : "Add horizontal/vertical constraint";
2939
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", cmdName));
2940
for (auto& geoId : edgegeoids) {
2941
std::string typeToApply = type;
2942
if (type == "HorVer") {
2943
const Part::Geometry* geo = Obj->getGeometry(geoId);
2944
auto* line = static_cast<const Part::GeomLineSegment*>(geo);
2945
double angle = toVector2d(line->getEndPoint() - line->getStartPoint()).Angle();
2946
typeToApply = fabs(sin(angle)) < fabs(cos(angle)) ? "Horizontal" : "Vertical";
2949
Gui::cmdAppObjectArgs(selection[0].getObject(),
2950
"addConstraint(Sketcher.Constraint('%s',%d))",
2955
else if (fixedpoints <= 1) {
2957
const char* cmdName = type == "Horizontal" ? "Add horizontal alignment" : type == "Vertical" ? "Add vertical alignment" : "Add horizontal/vertical alignment";
2958
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", cmdName));
2959
std::vector<int>::iterator it;
2960
std::vector<Sketcher::PointPos>::iterator itp;
2961
for (it = pointgeoids.begin(), itp = pointpos.begin();
2962
it != std::prev(pointgeoids.end()) && itp != std::prev(pointpos.end());
2965
std::string typeToApply = type;
2966
if (type == "HorVer") {
2967
auto point1 = Obj->getPoint(*it, *itp);
2968
auto point2 = Obj->getPoint(*std::next(it), *std::next(itp));
2969
double angle = toVector2d(point2 - point1).Angle();
2970
typeToApply = fabs(sin(angle)) < fabs(cos(angle)) ? "Horizontal" : "Vertical";
2973
Gui::cmdAppObjectArgs(selection[0].getObject(),
2974
"addConstraint(Sketcher.Constraint('%s',%d,%d,%d,%d))",
2977
static_cast<int>(*itp),
2979
static_cast<int>(*std::next(itp)));
2983
Gui::TranslatedUserWarning(Obj,
2984
QObject::tr("Impossible constraint"),
2985
QObject::tr("There are more than one fixed points selected. "
2986
"Select a maximum of one fixed point!"));
2990
Gui::Command::commitCommand();
2992
tryAutoRecompute(Obj);
2995
Gui::Command::getSelection().clearSelection();
2998
void horVerApplyConstraint(CmdSketcherConstraint* cmd, std::string type, std::vector<SelIdPair>& selSeq, int seqIndex)
3001
static_cast<SketcherGui::ViewProviderSketch*>(cmd->getActiveGuiDocument()->getInEdit());
3002
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
3007
if (selSeq.empty()) {
3011
int CrvId = selSeq.front().GeoId;
3013
const Part::Geometry* geo = Obj->getGeometry(CrvId);
3015
if (!isLineSegment(*geo)) {
3016
Gui::TranslatedUserWarning(
3018
QObject::tr("Impossible constraint"),
3019
QObject::tr("The selected edge is not a line segment."));
3024
if (!canHorVerBlock(Obj, CrvId)) {
3028
std::string typeToApply = type;
3029
if (type == "HorVer") {
3030
const Part::Geometry* geo = Obj->getGeometry(CrvId);
3031
auto* line = static_cast<const Part::GeomLineSegment*>(geo);
3032
double angle = toVector2d(line->getEndPoint() - line->getStartPoint()).Angle();
3033
typeToApply = fabs(sin(angle)) < fabs(cos(angle)) ? "Horizontal" : "Vertical";
3036
const char* cmdName = typeToApply == "Horizontal" ? "Add horizontal constraint" : "Add vertical constraint";
3037
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", cmdName));
3040
Gui::cmdAppObjectArgs(sketchgui->getObject(),
3041
"addConstraint(Sketcher.Constraint('%s',%d))",
3045
Gui::Command::commitCommand();
3047
tryAutoRecompute(Obj);
3057
Sketcher::PointPos PosId1, PosId2;
3058
GeoId1 = selSeq.at(0).GeoId;
3059
GeoId2 = selSeq.at(1).GeoId;
3060
PosId1 = selSeq.at(0).PosId;
3061
PosId2 = selSeq.at(1).PosId;
3063
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
3064
showNoConstraintBetweenFixedGeometry(Obj);
3068
std::string typeToApply = type;
3069
if (type == "HorVer") {
3070
auto point1 = Obj->getPoint(GeoId1, PosId1);
3071
auto point2 = Obj->getPoint(GeoId2, PosId2);
3072
double angle = toVector2d(point2 - point1).Angle();
3073
typeToApply = fabs(sin(angle)) < fabs(cos(angle)) ? "Horizontal" : "Vertical";
3077
const char* cmdName = type == "Horizontal" ? "Add horizontal alignment" : "Add vertical alignment";
3078
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", cmdName));
3081
Gui::cmdAppObjectArgs(sketchgui->getObject(),
3082
"addConstraint(Sketcher.Constraint('%s',%d,%d,%d,%d))",
3085
static_cast<int>(PosId1),
3087
static_cast<int>(PosId2));
3089
Gui::Command::commitCommand();
3091
tryAutoRecompute(Obj);
3098
class CmdSketcherConstrainHorVer : public CmdSketcherConstraint
3101
CmdSketcherConstrainHorVer();
3102
~CmdSketcherConstrainHorVer() override
3104
const char* className() const override
3106
return "CmdSketcherConstrainHorVer";
3110
void activated(int iMsg) override;
3111
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
3114
CmdSketcherConstrainHorVer::CmdSketcherConstrainHorVer()
3115
: CmdSketcherConstraint("Sketcher_ConstrainHorVer")
3117
sAppModule = "Sketcher";
3118
sGroup = "Sketcher";
3119
sMenuText = QT_TR_NOOP("Constrain horizontal/vertical");
3120
sToolTipText = QT_TR_NOOP("Constrains a single line to either horizontal or vertical, whichever is closer to current alignment.");
3121
sWhatsThis = "Sketcher_ConstrainHorVer";
3122
sStatusTip = sToolTipText;
3123
sPixmap = "Constraint_HorVer";
3127
allowedSelSequences = { {SelEdge}, {SelVertex, SelVertexOrRoot}, {SelRoot, SelVertex} };
3130
void CmdSketcherConstrainHorVer::activated(int iMsg)
3133
horVerActivated(this, "HorVer");
3136
void CmdSketcherConstrainHorVer::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
3138
horVerApplyConstraint(this, "HorVer", selSeq, seqIndex);
3144
class CmdSketcherConstrainHorizontal: public CmdSketcherConstraint
3147
CmdSketcherConstrainHorizontal();
3148
~CmdSketcherConstrainHorizontal() override
3150
const char* className() const override
3152
return "CmdSketcherConstrainHorizontal";
3156
void activated(int iMsg) override;
3157
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
3160
CmdSketcherConstrainHorizontal::CmdSketcherConstrainHorizontal()
3161
: CmdSketcherConstraint("Sketcher_ConstrainHorizontal")
3163
sAppModule = "Sketcher";
3164
sGroup = "Sketcher";
3165
sMenuText = QT_TR_NOOP("Constrain horizontal");
3166
sToolTipText = QT_TR_NOOP("Create a horizontal constraint on the selected item");
3167
sWhatsThis = "Sketcher_ConstrainHorizontal";
3168
sStatusTip = sToolTipText;
3169
sPixmap = "Constraint_Horizontal";
3173
allowedSelSequences = {{SelEdge}, {SelVertex, SelVertexOrRoot}, {SelRoot, SelVertex}};
3176
void CmdSketcherConstrainHorizontal::activated(int iMsg)
3179
horVerActivated(this, "Horizontal");
3182
void CmdSketcherConstrainHorizontal::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
3184
horVerApplyConstraint(this, "Horizontal", selSeq, seqIndex);
3189
class CmdSketcherConstrainVertical: public CmdSketcherConstraint
3192
CmdSketcherConstrainVertical();
3193
~CmdSketcherConstrainVertical() override
3195
const char* className() const override
3197
return "CmdSketcherConstrainVertical";
3201
void activated(int iMsg) override;
3202
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
3205
CmdSketcherConstrainVertical::CmdSketcherConstrainVertical()
3206
: CmdSketcherConstraint("Sketcher_ConstrainVertical")
3208
sAppModule = "Sketcher";
3209
sGroup = "Sketcher";
3210
sMenuText = QT_TR_NOOP("Constrain vertical");
3211
sToolTipText = QT_TR_NOOP("Create a vertical constraint on the selected item");
3212
sWhatsThis = "Sketcher_ConstrainVertical";
3213
sStatusTip = sToolTipText;
3214
sPixmap = "Constraint_Vertical";
3218
allowedSelSequences = {{SelEdge}, {SelVertex, SelVertexOrRoot}, {SelRoot, SelVertex}};
3221
void CmdSketcherConstrainVertical::activated(int iMsg)
3224
horVerActivated(this, "Vertical");
3227
void CmdSketcherConstrainVertical::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
3229
horVerApplyConstraint(this, "Vertical", selSeq, seqIndex);
3234
class CmdSketcherConstrainLock: public CmdSketcherConstraint
3237
CmdSketcherConstrainLock();
3238
~CmdSketcherConstrainLock() override
3240
void updateAction(int mode) override;
3241
const char* className() const override
3243
return "CmdSketcherConstrainLock";
3247
void activated(int iMsg) override;
3248
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
3251
CmdSketcherConstrainLock::CmdSketcherConstrainLock()
3252
: CmdSketcherConstraint("Sketcher_ConstrainLock")
3254
sAppModule = "Sketcher";
3255
sGroup = "Sketcher";
3256
sMenuText = QT_TR_NOOP("Constrain lock");
3257
sToolTipText = QT_TR_NOOP("Create both a horizontal "
3258
"and a vertical distance constraint\n"
3259
"on the selected vertex");
3260
sWhatsThis = "Sketcher_ConstrainLock";
3261
sStatusTip = sToolTipText;
3262
sPixmap = "Constraint_Lock";
3266
allowedSelSequences = {{SelVertex}};
3269
void CmdSketcherConstrainLock::activated(int iMsg)
3274
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
3277
if (selection.size() != 1
3278
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
3279
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
3280
"User parameter:BaseApp/Preferences/Mod/Sketcher");
3281
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
3283
if (constraintMode) {
3284
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
3285
getSelection().clearSelection();
3288
Gui::TranslatedUserWarning(getActiveGuiDocument(),
3289
QObject::tr("Wrong selection"),
3290
QObject::tr("Select vertices from the sketch."));
3296
const std::vector<std::string>& SubNames = selection[0].getSubNames();
3297
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
3299
std::vector<int> GeoId;
3300
std::vector<Sketcher::PointPos> PosId;
3302
for (std::vector<std::string>::const_iterator it = SubNames.begin(); it != SubNames.end();
3305
Sketcher::PointPos PosIdt;
3306
getIdsFromName((*it), Obj, GeoIdt, PosIdt);
3307
GeoId.push_back(GeoIdt);
3308
PosId.push_back(PosIdt);
3310
if ((it != std::prev(SubNames.end())
3311
&& (isEdge(GeoIdt, PosIdt) || (GeoIdt < 0 && GeoIdt >= Sketcher::GeoEnum::VAxis)))
3312
|| (it == std::prev(SubNames.end()) && isEdge(GeoIdt, PosIdt))) {
3313
if (selection.size() == 1) {
3314
Gui::TranslatedUserWarning(
3316
QObject::tr("Wrong selection"),
3317
QObject::tr("Select one vertex from the sketch other than the origin."));
3320
Gui::TranslatedUserWarning(Obj,
3321
QObject::tr("Wrong selection"),
3322
QObject::tr("Select only vertices from the sketch. The "
3323
"last selected vertex may be the origin."));
3326
getSelection().clearSelection();
3331
int lastconstraintindex = Obj->Constraints.getSize() - 1;
3333
if (GeoId.size() == 1) {
3335
bool edgeisblocked = false;
3337
if (isPointOrSegmentFixed(Obj, GeoId[0])) {
3338
edgeisblocked = true;
3341
Base::Vector3d pnt = Obj->getPoint(GeoId[0], PosId[0]);
3344
openCommand(QT_TRANSLATE_NOOP("Command", "Add 'Lock' constraint"));
3345
Gui::cmdAppObjectArgs(selection[0].getObject(),
3346
"addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%f))",
3348
static_cast<int>(PosId[0]),
3350
Gui::cmdAppObjectArgs(selection[0].getObject(),
3351
"addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%f))",
3353
static_cast<int>(PosId[0]),
3356
lastconstraintindex += 2;
3358
if (edgeisblocked || GeoId[0] <= Sketcher::GeoEnum::RefExt
3359
|| constraintCreationMode == Reference) {
3362
Gui::cmdAppObjectArgs(selection[0].getObject(),
3363
"setDriving(%d,%s)",
3364
lastconstraintindex - 1,
3367
Gui::cmdAppObjectArgs(selection[0].getObject(),
3368
"setDriving(%d,%s)",
3369
lastconstraintindex,
3374
std::vector<int>::const_iterator itg;
3375
std::vector<Sketcher::PointPos>::const_iterator itp;
3377
Base::Vector3d pntr = Obj->getPoint(GeoId.back(), PosId.back());
3380
bool refpointfixed = false;
3382
if (isPointOrSegmentFixed(Obj, GeoId.back())) {
3383
refpointfixed = true;
3386
for (itg = GeoId.begin(), itp = PosId.begin();
3387
itg != std::prev(GeoId.end()) && itp != std::prev(PosId.end());
3389
bool pointfixed = false;
3391
if (isPointOrSegmentFixed(Obj, *itg)) {
3395
Base::Vector3d pnt = Obj->getPoint(*itg, *itp);
3398
openCommand(QT_TRANSLATE_NOOP("Command", "Add relative 'Lock' constraint"));
3399
Gui::cmdAppObjectArgs(selection[0].getObject(),
3400
"addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%d,%d,%f))",
3402
static_cast<int>(*itp),
3404
static_cast<int>(PosId.back()),
3407
Gui::cmdAppObjectArgs(selection[0].getObject(),
3408
"addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%d,%d,%f))",
3410
static_cast<int>(*itp),
3412
static_cast<int>(PosId.back()),
3414
lastconstraintindex += 2;
3416
if ((refpointfixed && pointfixed) || constraintCreationMode == Reference) {
3419
Gui::cmdAppObjectArgs(selection[0].getObject(),
3420
"setDriving(%d,%s)",
3421
lastconstraintindex - 1,
3424
Gui::cmdAppObjectArgs(selection[0].getObject(),
3425
"setDriving(%d,%s)",
3426
lastconstraintindex,
3434
tryAutoRecompute(Obj);
3437
getSelection().clearSelection();
3440
void CmdSketcherConstrainLock::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
3445
SketcherGui::ViewProviderSketch* sketchgui =
3446
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
3447
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
3450
bool pointfixed = false;
3452
if (selSeq.empty()) {
3456
if (isPointOrSegmentFixed(Obj, selSeq.front().GeoId)) {
3460
Base::Vector3d pnt = Obj->getPoint(selSeq.front().GeoId, selSeq.front().PosId);
3463
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add fixed constraint"));
3464
Gui::cmdAppObjectArgs(sketchgui->getObject(),
3465
"addConstraint(Sketcher.Constraint('DistanceX', %d, %d, %f))",
3466
selSeq.front().GeoId,
3467
static_cast<int>(selSeq.front().PosId),
3469
Gui::cmdAppObjectArgs(sketchgui->getObject(),
3470
"addConstraint(Sketcher.Constraint('DistanceY', %d, %d, %f))",
3471
selSeq.front().GeoId,
3472
static_cast<int>(selSeq.front().PosId),
3475
if (pointfixed || constraintCreationMode == Reference) {
3477
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
3479
Gui::cmdAppObjectArgs(sketchgui->getObject(),
3480
"setDriving(%d, %s)",
3484
Gui::cmdAppObjectArgs(sketchgui->getObject(),
3485
"setDriving(%d, %s)",
3491
Gui::Command::commitCommand();
3493
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
3494
"User parameter:BaseApp/Preferences/Mod/Sketcher");
3495
bool autoRecompute = hGrp->GetBool("AutoRecompute", false);
3497
if (autoRecompute) {
3498
Gui::Command::updateActive();
3504
void CmdSketcherConstrainLock::updateAction(int mode)
3509
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Lock_Driven"));
3514
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Lock"));
3522
class CmdSketcherConstrainBlock: public CmdSketcherConstraint
3525
CmdSketcherConstrainBlock();
3526
~CmdSketcherConstrainBlock() override
3528
const char* className() const override
3530
return "CmdSketcherConstrainBlock";
3534
void activated(int iMsg) override;
3535
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
3538
CmdSketcherConstrainBlock::CmdSketcherConstrainBlock()
3539
: CmdSketcherConstraint("Sketcher_ConstrainBlock")
3541
sAppModule = "Sketcher";
3542
sGroup = "Sketcher";
3543
sMenuText = QT_TR_NOOP("Constrain block");
3544
sToolTipText = QT_TR_NOOP("Block the selected edge from moving");
3545
sWhatsThis = "Sketcher_ConstrainBlock";
3546
sStatusTip = sToolTipText;
3547
sPixmap = "Constraint_Block";
3551
allowedSelSequences = {{SelEdge}};
3554
void CmdSketcherConstrainBlock::activated(int iMsg)
3559
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
3562
if (selection.size() != 1
3563
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
3564
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
3565
"User parameter:BaseApp/Preferences/Mod/Sketcher");
3566
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
3568
if (constraintMode) {
3569
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
3570
getSelection().clearSelection();
3573
Gui::TranslatedUserWarning(getActiveGuiDocument(),
3574
QObject::tr("Wrong selection"),
3575
QObject::tr("Select vertices from the sketch."));
3581
const std::vector<std::string>& SubNames = selection[0].getSubNames();
3582
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
3585
if (Obj->getLastSolverStatus() != GCS::Success || Obj->getLastHasConflicts()
3586
|| Obj->getLastHasRedundancies()) {
3587
Gui::TranslatedUserWarning(Obj,
3588
QObject::tr("Wrong solver status"),
3589
QObject::tr("A Block constraint cannot be added "
3590
"if the sketch is unsolved "
3591
"or there are redundant and "
3592
"conflicting constraints."));
3596
std::vector<int> GeoId;
3597
const std::vector<Sketcher::Constraint*>& vals = Obj->Constraints.getValues();
3599
for (auto& subname : SubNames) {
3601
Sketcher::PointPos PosIdt;
3602
getIdsFromName(subname, Obj, GeoIdt, PosIdt);
3604
if (isVertex(GeoIdt, PosIdt) || GeoIdt < 0) {
3605
if (selection.size() == 1) {
3606
Gui::TranslatedUserWarning(Obj,
3607
QObject::tr("Wrong selection"),
3608
QObject::tr("Select one edge from the sketch."));
3611
Gui::TranslatedUserWarning(Obj,
3612
QObject::tr("Wrong selection"),
3613
QObject::tr("Select only edges from the sketch."));
3616
getSelection().clearSelection();
3621
if (checkConstraint(vals, Sketcher::Block, GeoIdt, Sketcher::PointPos::none)) {
3622
Gui::TranslatedUserWarning(
3624
QObject::tr("Double constraint"),
3625
QObject::tr("The selected edge already has a Block constraint!"));
3629
GeoId.push_back(GeoIdt);
3632
for (std::vector<int>::iterator itg = GeoId.begin(); itg != GeoId.end(); ++itg) {
3634
openCommand(QT_TRANSLATE_NOOP("Command", "Add 'Block' constraint"));
3636
bool safe = addConstraintSafely(Obj, [&]() {
3637
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Block',%d))", (*itg));
3645
tryAutoRecompute(Obj);
3650
getSelection().clearSelection();
3653
void CmdSketcherConstrainBlock::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
3659
SketcherGui::ViewProviderSketch* sketchgui =
3660
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
3662
auto Obj = static_cast<Sketcher::SketchObject*>(sketchgui->getObject());
3665
const std::vector<Sketcher::Constraint*>& vals = Obj->Constraints.getValues();
3667
if (selSeq.empty()) {
3671
if (checkConstraint(vals,
3673
selSeq.front().GeoId,
3674
Sketcher::PointPos::none)) {
3675
Gui::TranslatedUserWarning(
3677
QObject::tr("Double constraint"),
3678
QObject::tr("The selected edge already has a Block constraint!"));
3683
openCommand(QT_TRANSLATE_NOOP("Command", "Add block constraint"));
3685
bool safe = addConstraintSafely(Obj, [&]() {
3686
Gui::cmdAppObjectArgs(sketchgui->getObject(),
3687
"addConstraint(Sketcher.Constraint('Block',%d))",
3688
selSeq.front().GeoId);
3696
tryAutoRecompute(Obj);
3706
class CmdSketcherConstrainCoincidentUnified : public CmdSketcherConstraint
3709
CmdSketcherConstrainCoincidentUnified(const char* initName = "Sketcher_ConstrainCoincidentUnified");
3710
~CmdSketcherConstrainCoincidentUnified() override
3712
const char* className() const override
3714
return "CmdSketcherConstrainCoincidentUnified";
3718
enum class CoincicenceType {
3724
void activated(int iMsg) override;
3725
void onActivated(CoincicenceType type);
3726
void activatedCoincident(SketchObject* obj, std::vector<SelIdPair> points, std::vector<SelIdPair> curves);
3727
void activatedPointOnObject(SketchObject* obj, std::vector<SelIdPair> points, std::vector<SelIdPair> curves);
3729
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
3730
void applyConstraintCoincident(std::vector<SelIdPair>& selSeq, int seqIndex);
3731
void applyConstraintPointOnObject(std::vector<SelIdPair>& selSeq, int seqIndex);
3734
static bool substituteConstraintCombinationsPointOnObject(SketchObject* Obj, int GeoId1, PointPos PosId1, int GeoId2);
3735
static bool substituteConstraintCombinationsCoincident(SketchObject* Obj, int GeoId1, PointPos PosId1, int GeoId2, PointPos PosId2);
3738
CmdSketcherConstrainCoincidentUnified::CmdSketcherConstrainCoincidentUnified(const char* initName)
3739
: CmdSketcherConstraint(initName)
3741
sAppModule = "Sketcher";
3742
sGroup = "Sketcher";
3743
sMenuText = QT_TR_NOOP("Constrain coincident");
3744
sToolTipText = QT_TR_NOOP("Create a coincident constraint between points, or fix a point on an edge, "
3745
"or a concentric constraint between circles, arcs, and ellipses");
3746
sWhatsThis = "Sketcher_ConstrainCoincidentUnified";
3747
sStatusTip = sToolTipText;
3748
sPixmap = "Constraint_Coincident";
3750
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
3751
"User parameter:BaseApp/Preferences/Mod/Sketcher/Constraints");
3752
sAccel = hGrp->GetBool("UnifiedCoincident", true) ? "C" :"C,O";
3756
allowedSelSequences = { {SelVertex, SelEdgeOrAxis},
3758
{SelVertex, SelExternalEdge},
3759
{SelEdge, SelVertexOrRoot},
3760
{SelEdgeOrAxis, SelVertex},
3761
{SelExternalEdge, SelVertex},
3763
{SelVertex, SelVertexOrRoot},
3764
{SelRoot, SelVertex},
3766
{SelEdge, SelExternalEdge},
3767
{SelExternalEdge, SelEdge} };
3770
bool CmdSketcherConstrainCoincidentUnified::substituteConstraintCombinationsPointOnObject(SketchObject* Obj, int GeoId1, PointPos PosId1, int GeoId2)
3772
const std::vector<Constraint*>& cvals = Obj->Constraints.getValues();
3775
for (std::vector<Constraint*>::const_iterator it = cvals.begin(); it != cvals.end();
3777
if ((*it)->Type == Sketcher::Tangent && (*it)->FirstPos == Sketcher::PointPos::none
3778
&& (*it)->SecondPos == Sketcher::PointPos::none && (*it)->Third == GeoEnum::GeoUndef
3779
&& (((*it)->First == GeoId1 && (*it)->Second == GeoId2)
3780
|| ((*it)->Second == GeoId1 && (*it)->First == GeoId2))
3781
&& (PosId1 == Sketcher::PointPos::start
3782
|| PosId1 == Sketcher::PointPos::end)) {
3787
Gui::cmdAppObjectArgs(Obj, "delConstraint(%d)", cid);
3789
doEndpointToEdgeTangency(Obj, GeoId1, PosId1, GeoId2);
3791
notifyConstraintSubstitutions(
3792
QObject::tr("Endpoint to edge tangency was applied instead."));
3794
getSelection().clearSelection();
3802
bool CmdSketcherConstrainCoincidentUnified::substituteConstraintCombinationsCoincident(SketchObject* Obj, int GeoId1, PointPos PosId1, int GeoId2, PointPos PosId2)
3805
bool constraintExists = Obj->arePointsCoincident(GeoId1, PosId1, GeoId2, PosId2);
3807
const std::vector<Constraint*>& cvals = Obj->Constraints.getValues();
3813
for (std::vector<Constraint*>::const_iterator it = cvals.begin(); it != cvals.end();
3815
if ((*it)->Type == Sketcher::Tangent && (*it)->Third == GeoEnum::GeoUndef
3816
&& (((*it)->First == GeoId1 && (*it)->Second == GeoId2)
3817
|| ((*it)->Second == GeoId1 && (*it)->First == GeoId2))) {
3818
if (!(PosId1 == Sketcher::PointPos::start
3819
|| PosId1 == Sketcher::PointPos::end)
3820
|| !(PosId2 == Sketcher::PointPos::start
3821
|| PosId2 == Sketcher::PointPos::end)) {
3824
if ((*it)->FirstPos == Sketcher::PointPos::none
3825
&& (*it)->SecondPos == Sketcher::PointPos::none) {
3827
if (constraintExists) {
3829
Gui::cmdAppObjectArgs(Obj,
3830
"delConstraintOnPoint(%d,%d)",
3832
static_cast<int>(PosId1));
3835
Gui::cmdAppObjectArgs(Obj, "delConstraint(%d)", j);
3837
doEndpointTangency(Obj, GeoId1, GeoId2, PosId1, PosId2);
3839
notifyConstraintSubstitutions(
3840
QObject::tr("Endpoint to endpoint tangency was applied instead."));
3842
getSelection().clearSelection();
3845
else if (isBsplineKnot(Obj, GeoId1) != isBsplineKnot(Obj, GeoId2)) {
3848
if (isBsplineKnot(Obj, GeoId2)) {
3849
std::swap(GeoId1, GeoId2);
3850
std::swap(PosId1, PosId2);
3854
if ((*it)->SecondPos == Sketcher::PointPos::none) {
3855
Gui::cmdAppObjectArgs(Obj, "delConstraint(%d)", j);
3857
doEndpointTangency(Obj, GeoId1, GeoId2, PosId1, PosId2);
3859
notifyConstraintSubstitutions(
3860
QObject::tr("B-spline knot to endpoint tangency was applied instead."));
3862
getSelection().clearSelection();
3872
void CmdSketcherConstrainCoincidentUnified::activated(int iMsg)
3875
onActivated(CoincicenceType::Both);
3878
void CmdSketcherConstrainCoincidentUnified::onActivated(CoincicenceType type)
3881
if (type == CoincicenceType::Coincident) {
3882
errorMess = QObject::tr("Select either several points, or several conics for concentricity.");
3884
else if (type == CoincicenceType::PointOnObject) {
3885
errorMess = QObject::tr("Select either one point and several curves, or one curve and several points");
3888
errorMess = QObject::tr("Select either one point and several curves or one curve and several"
3889
" points for pointOnObject, or several points for coincidence, or several conics for concentricity.");
3893
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
3896
if (selection.size() != 1
3897
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
3898
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
3899
"User parameter:BaseApp/Preferences/Mod/Sketcher");
3900
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
3902
if (constraintMode) {
3903
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
3904
getSelection().clearSelection();
3907
Gui::TranslatedUserWarning(getActiveGuiDocument(), QObject::tr("Wrong selection"), errorMess);
3913
const std::vector<std::string>& SubNames = selection[0].getSubNames();
3914
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
3917
std::vector<SelIdPair> points;
3918
std::vector<SelIdPair> curves;
3919
for (std::size_t i = 0; i < SubNames.size(); i++) {
3921
getIdsFromName(SubNames[i], Obj, id.GeoId, id.PosId);
3922
if (isEdge(id.GeoId, id.PosId)) {
3923
curves.push_back(id);
3925
if (isVertex(id.GeoId, id.PosId)) {
3926
points.push_back(id);
3930
if (type != CoincicenceType::Coincident && ((points.size() == 1 && !curves.empty()) || (!points.empty() && curves.size() == 1))) {
3931
activatedPointOnObject(Obj, points, curves);
3933
else if (type != CoincicenceType::PointOnObject && ((!points.empty() && curves.empty()) || (points.empty() && !curves.empty()))) {
3934
activatedCoincident(Obj, points, curves);
3937
Gui::TranslatedUserWarning(Obj, QObject::tr("Wrong selection"), errorMess);
3941
void CmdSketcherConstrainCoincidentUnified::activatedPointOnObject(SketchObject* obj, std::vector<SelIdPair> points, std::vector<SelIdPair> curves)
3943
openCommand(QT_TRANSLATE_NOOP("Command", "Add point on object constraint"));
3945
for (std::size_t iPnt = 0; iPnt < points.size(); iPnt++) {
3946
for (std::size_t iCrv = 0; iCrv < curves.size(); iCrv++) {
3947
if (areBothPointsOrSegmentsFixed(obj, points[iPnt].GeoId, curves[iCrv].GeoId)) {
3948
showNoConstraintBetweenFixedGeometry(obj);
3951
if (points[iPnt].GeoId == curves[iCrv].GeoId) {
3955
const Part::Geometry* geom = obj->getGeometry(curves[iCrv].GeoId);
3957
if (geom && isBsplinePole(geom)) {
3958
Gui::TranslatedUserWarning(
3960
QObject::tr("Wrong selection"),
3961
QObject::tr("Select an edge that is not a B-spline weight."));
3967
if (substituteConstraintCombinationsPointOnObject(obj,
3970
curves[iCrv].GeoId)) {
3976
Gui::cmdAppObjectArgs(
3978
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
3980
static_cast<int>(points[iPnt].PosId),
3981
curves[iCrv].GeoId);
3986
getSelection().clearSelection();
3990
Gui::TranslatedUserWarning(obj,
3991
QObject::tr("Wrong selection"),
3992
QObject::tr("None of the selected points were constrained onto the respective curves, because they are part of the same element, they are both external geometry, or the edge is not eligible."));
3997
void CmdSketcherConstrainCoincidentUnified::activatedCoincident(SketchObject* obj, std::vector<SelIdPair> points, std::vector<SelIdPair> curves)
3999
bool allConicsEdges = true;
4001
for (auto& curve : curves) {
4002
if (!isGeoConcentricCompatible(obj->getGeometry(curve.GeoId))) {
4003
allConicsEdges = false;
4006
if (!allConicsEdges) {
4007
Gui::TranslatedUserWarning(
4009
QObject::tr("Wrong selection"),
4010
QObject::tr("Select two or more vertices from the sketch for a coincident "
4011
"constraint, or two or more circles, ellipses, arcs or arcs of ellipse "
4012
"for a concentric constraint."));
4015
curve.PosId = Sketcher::PointPos::mid;
4018
std::vector<SelIdPair> vecOfSelIdToUse = curves.empty() ? points : curves;
4020
int GeoId1 = vecOfSelIdToUse[0].GeoId;
4021
Sketcher::PointPos PosId1 = vecOfSelIdToUse[0].PosId;
4024
bool constraintsAdded = false;
4025
openCommand(QT_TRANSLATE_NOOP("Command", "Add coincident constraint"));
4027
for (std::size_t i = 1; i < vecOfSelIdToUse.size(); i++) {
4028
int GeoId2 = vecOfSelIdToUse[i].GeoId;
4029
Sketcher::PointPos PosId2 = vecOfSelIdToUse[i].PosId;
4032
if (areBothPointsOrSegmentsFixed(obj, GeoId1, GeoId2)) {
4033
showNoConstraintBetweenFixedGeometry(obj);
4041
if (substituteConstraintCombinationsCoincident(obj, GeoId1, PosId1, GeoId2, PosId2)) {
4042
constraintsAdded = true;
4047
bool constraintExists = obj->arePointsCoincident(GeoId1, PosId1, GeoId2, PosId2);
4049
if (!constraintExists) {
4050
constraintsAdded = true;
4051
Gui::cmdAppObjectArgs(obj,
4052
"addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d))",
4054
static_cast<int>(PosId1),
4056
static_cast<int>(PosId2));
4061
if (constraintsAdded) {
4068
tryAutoRecompute(obj);
4071
getSelection().clearSelection();
4074
void CmdSketcherConstrainCoincidentUnified::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
4083
applyConstraintPointOnObject(selSeq, seqIndex);
4091
applyConstraintCoincident(selSeq, seqIndex);
4098
void CmdSketcherConstrainCoincidentUnified::applyConstraintPointOnObject(std::vector<SelIdPair>& selSeq, int seqIndex)
4100
int GeoIdVt, GeoIdCrv;
4101
Sketcher::PointPos PosIdVt;
4107
GeoIdVt = selSeq.at(0).GeoId;
4108
GeoIdCrv = selSeq.at(1).GeoId;
4109
PosIdVt = selSeq.at(0).PosId;
4115
GeoIdVt = selSeq.at(1).GeoId;
4116
GeoIdCrv = selSeq.at(0).GeoId;
4117
PosIdVt = selSeq.at(1).PosId;
4124
SketcherGui::ViewProviderSketch* sketchgui =
4125
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
4126
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
4128
openCommand(QT_TRANSLATE_NOOP("Command", "Add point on object constraint"));
4131
if (areBothPointsOrSegmentsFixed(Obj, GeoIdVt, GeoIdCrv)) {
4132
showNoConstraintBetweenFixedGeometry(Obj);
4135
if (GeoIdVt == GeoIdCrv) {
4139
const Part::Geometry* geom = Obj->getGeometry(GeoIdCrv);
4141
if (geom && isBsplinePole(geom)) {
4142
Gui::TranslatedUserWarning(Obj,
4143
QObject::tr("Wrong selection"),
4144
QObject::tr("Select an edge that is not a B-spline weight."));
4151
if (!substituteConstraintCombinationsPointOnObject(Obj, GeoIdVt, PosIdVt, GeoIdCrv)) {
4152
Gui::cmdAppObjectArgs(sketchgui->getObject(),
4153
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
4155
static_cast<int>(PosIdVt),
4160
tryAutoRecompute(Obj);
4164
Gui::TranslatedUserWarning(Obj,
4165
QObject::tr("Wrong selection"),
4166
QObject::tr("None of the selected points "
4167
"were constrained onto the respective curves, "
4168
"either because they are parts of the same element, "
4169
"or because they are both external geometry."));
4174
void CmdSketcherConstrainCoincidentUnified::applyConstraintCoincident(std::vector<SelIdPair>& selSeq, int seqIndex)
4176
SketcherGui::ViewProviderSketch* sketchgui =
4177
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
4178
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
4180
int GeoId1 = selSeq.at(0).GeoId, GeoId2 = selSeq.at(1).GeoId;
4181
Sketcher::PointPos PosId1 = selSeq.at(0).PosId, PosId2 = selSeq.at(1).PosId;
4192
if (!isGeoConcentricCompatible(Obj->getGeometry(GeoId1))
4193
|| !isGeoConcentricCompatible(Obj->getGeometry(GeoId2))) {
4194
Gui::TranslatedUserWarning(
4196
QObject::tr("Wrong selection"),
4198
"Select two vertices from the sketch for a coincident constraint, or two "
4199
"circles, ellipses, arcs or arcs of ellipse for a concentric constraint."));
4202
PosId1 = Sketcher::PointPos::mid;
4203
PosId2 = Sketcher::PointPos::mid;
4208
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
4209
showNoConstraintBetweenFixedGeometry(Obj);
4214
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add coincident constraint"));
4217
bool constraintExists = Obj->arePointsCoincident(GeoId1, PosId1, GeoId2, PosId2);
4218
if (substituteConstraintCombinationsCoincident(Obj, GeoId1, PosId1, GeoId2, PosId2)) {}
4219
else if (!constraintExists && (GeoId1 != GeoId2)) {
4220
Gui::cmdAppObjectArgs(sketchgui->getObject(),
4221
"addConstraint(Sketcher.Constraint('Coincident', %d, %d, %d, %d))",
4223
static_cast<int>(PosId1),
4225
static_cast<int>(PosId2));
4228
Gui::Command::abortCommand();
4231
Gui::Command::commitCommand();
4232
tryAutoRecompute(Obj);
4238
class CmdSketcherConstrainCoincident: public CmdSketcherConstrainCoincidentUnified
4241
CmdSketcherConstrainCoincident();
4242
~CmdSketcherConstrainCoincident() override
4244
const char* className() const override
4246
return "CmdSketcherConstrainCoincident";
4250
void activated(int iMsg) override;
4251
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
4254
CmdSketcherConstrainCoincident::CmdSketcherConstrainCoincident()
4255
: CmdSketcherConstrainCoincidentUnified("Sketcher_ConstrainCoincident")
4257
sAppModule = "Sketcher";
4258
sGroup = "Sketcher";
4259
sMenuText = QT_TR_NOOP("Constrain coincident");
4260
sToolTipText = QT_TR_NOOP("Create a coincident constraint between points, or a concentric "
4261
"constraint between circles, arcs, and ellipses");
4262
sWhatsThis = "Sketcher_ConstrainCoincident";
4263
sStatusTip = sToolTipText;
4264
sPixmap = "Constraint_PointOnPoint";
4265
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
4266
"User parameter:BaseApp/Preferences/Mod/Sketcher/Constraints");
4267
sAccel = hGrp->GetBool("UnifiedCoincident", true) ? "C,C" : "C";
4270
allowedSelSequences = {{SelVertex, SelVertexOrRoot},
4271
{SelRoot, SelVertex},
4273
{SelEdge, SelExternalEdge},
4274
{SelExternalEdge, SelEdge}};
4277
void CmdSketcherConstrainCoincident::activated(int iMsg)
4280
onActivated(CoincicenceType::Coincident);
4283
void CmdSketcherConstrainCoincident::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
4285
applyConstraintCoincident(selSeq, seqIndex);
4290
class CmdSketcherConstrainPointOnObject: public CmdSketcherConstrainCoincidentUnified
4293
CmdSketcherConstrainPointOnObject();
4294
~CmdSketcherConstrainPointOnObject() override
4296
const char* className() const override
4298
return "CmdSketcherConstrainPointOnObject";
4302
void activated(int iMsg) override;
4303
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
4306
CmdSketcherConstrainPointOnObject::CmdSketcherConstrainPointOnObject()
4307
: CmdSketcherConstrainCoincidentUnified("Sketcher_ConstrainPointOnObject")
4309
sAppModule = "Sketcher";
4310
sGroup = "Sketcher";
4311
sMenuText = QT_TR_NOOP("Constrain point on object");
4312
sToolTipText = QT_TR_NOOP("Fix a point onto an object");
4313
sWhatsThis = "Sketcher_ConstrainPointOnObject";
4314
sStatusTip = sToolTipText;
4315
sPixmap = "Constraint_PointOnObject";
4319
allowedSelSequences = {{SelVertex, SelEdgeOrAxis},
4321
{SelVertex, SelExternalEdge},
4322
{SelEdge, SelVertexOrRoot},
4323
{SelEdgeOrAxis, SelVertex},
4324
{SelExternalEdge, SelVertex}};
4327
void CmdSketcherConstrainPointOnObject::activated(int iMsg)
4330
onActivated(CoincicenceType::PointOnObject);
4333
void CmdSketcherConstrainPointOnObject::applyConstraint(std::vector<SelIdPair>& selSeq,
4336
applyConstraintPointOnObject(selSeq, seqIndex);
4341
class CmdSketcherConstrainDistance : public CmdSketcherConstraint
4344
CmdSketcherConstrainDistance();
4345
~CmdSketcherConstrainDistance() override
4347
void updateAction(int mode) override;
4348
const char* className() const override
4350
return "CmdSketcherConstrainDistance";
4354
void activated(int iMsg) override;
4355
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
4358
CmdSketcherConstrainDistance::CmdSketcherConstrainDistance()
4359
: CmdSketcherConstraint("Sketcher_ConstrainDistance")
4361
sAppModule = "Sketcher";
4362
sGroup = "Sketcher";
4363
sMenuText = QT_TR_NOOP("Constrain distance");
4364
sToolTipText = QT_TR_NOOP("Fix a length of a line or the distance between a line and a vertex "
4365
"or between two circles");
4366
sWhatsThis = "Sketcher_ConstrainDistance";
4367
sStatusTip = sToolTipText;
4368
sPixmap = "Constraint_Length";
4372
allowedSelSequences = { {SelVertex, SelVertexOrRoot},
4373
{SelRoot, SelVertex},
4376
{SelVertex, SelEdgeOrAxis},
4378
{SelVertex, SelExternalEdge},
4379
{SelRoot, SelExternalEdge},
4380
{SelEdge, SelEdge} };
4383
void CmdSketcherConstrainDistance::activated(int iMsg)
4387
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
4390
if (selection.size() != 1
4391
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
4392
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
4393
"User parameter:BaseApp/Preferences/Mod/Sketcher");
4394
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
4396
if (constraintMode) {
4397
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
4399
getSelection().clearSelection();
4402
Gui::TranslatedUserWarning(getActiveGuiDocument(),
4403
QObject::tr("Wrong selection"),
4404
QObject::tr("Select vertices from the sketch."));
4410
const std::vector<std::string>& SubNames = selection[0].getSubNames();
4411
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
4413
if (SubNames.empty() || SubNames.size() > 2) {
4414
Gui::TranslatedUserWarning(Obj,
4415
QObject::tr("Wrong selection"),
4416
QObject::tr("Select exactly one line or one point and one line "
4417
"or two points from the sketch."));
4421
int GeoId1, GeoId2 = GeoEnum::GeoUndef;
4422
Sketcher::PointPos PosId1, PosId2 = Sketcher::PointPos::none;
4423
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
4424
if (SubNames.size() == 2) {
4425
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
4428
bool arebothpointsorsegmentsfixed = areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2);
4430
if (isVertex(GeoId1, PosId1)
4431
&& (GeoId2 == Sketcher::GeoEnum::VAxis || GeoId2 == Sketcher::GeoEnum::HAxis)) {
4432
std::swap(GeoId1, GeoId2);
4433
std::swap(PosId1, PosId2);
4436
if ((isVertex(GeoId1, PosId1) || GeoId1 == Sketcher::GeoEnum::VAxis
4437
|| GeoId1 == Sketcher::GeoEnum::HAxis)
4438
&& isVertex(GeoId2, PosId2)) {
4440
Base::Vector3d pnt2 = Obj->getPoint(GeoId2, PosId2);
4442
if (GeoId1 == Sketcher::GeoEnum::HAxis && PosId1 == Sketcher::PointPos::none) {
4443
PosId1 = Sketcher::PointPos::start;
4446
QT_TRANSLATE_NOOP("Command", "Add distance from horizontal axis constraint"));
4447
Gui::cmdAppObjectArgs(selection[0].getObject(),
4448
"addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%d,%d,%f))",
4450
static_cast<int>(PosId1),
4452
static_cast<int>(PosId2),
4455
else if (GeoId1 == Sketcher::GeoEnum::VAxis && PosId1 == Sketcher::PointPos::none) {
4456
PosId1 = Sketcher::PointPos::start;
4458
openCommand(QT_TRANSLATE_NOOP("Command", "Add distance from vertical axis constraint"));
4459
Gui::cmdAppObjectArgs(selection[0].getObject(),
4460
"addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%d,%d,%f))",
4462
static_cast<int>(PosId1),
4464
static_cast<int>(PosId2),
4468
Base::Vector3d pnt1 = Obj->getPoint(GeoId1, PosId1);
4470
openCommand(QT_TRANSLATE_NOOP("Command", "Add point to point distance constraint"));
4471
Gui::cmdAppObjectArgs(selection[0].getObject(),
4472
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%d,%f))",
4474
static_cast<int>(PosId1),
4476
static_cast<int>(PosId2),
4477
(pnt2 - pnt1).Length());
4480
if (arebothpointsorsegmentsfixed || constraintCreationMode == Reference) {
4482
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
4484
Gui::cmdAppObjectArgs(selection[0].getObject(),
4485
"setDriving(%d,%s)",
4488
finishDatumConstraint(this, Obj, false);
4491
finishDatumConstraint(this, Obj, true);
4495
else if ((isVertex(GeoId1, PosId1) && isEdge(GeoId2, PosId2))
4496
|| (isEdge(GeoId1, PosId1) && isVertex(GeoId2, PosId2))) {
4497
if (isVertex(GeoId2, PosId2)) {
4498
std::swap(GeoId1, GeoId2);
4499
std::swap(PosId1, PosId2);
4501
Base::Vector3d pnt = Obj->getPoint(GeoId1, PosId1);
4502
const Part::Geometry* geom = Obj->getGeometry(GeoId2);
4504
if (isLineSegment(*geom)) {
4505
auto lineSeg = static_cast<const Part::GeomLineSegment*>(geom);
4506
Base::Vector3d pnt1 = lineSeg->getStartPoint();
4507
Base::Vector3d pnt2 = lineSeg->getEndPoint();
4508
Base::Vector3d d = pnt2 - pnt1;
4510
std::abs(-pnt.x * d.y + pnt.y * d.x + pnt1.x * pnt2.y - pnt2.x * pnt1.y)
4513
openCommand(QT_TRANSLATE_NOOP("Command", "Add point to line Distance constraint"));
4514
Gui::cmdAppObjectArgs(selection[0].getObject(),
4515
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%f))",
4517
static_cast<int>(PosId1),
4521
if (arebothpointsorsegmentsfixed
4522
|| constraintCreationMode
4524
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
4526
Gui::cmdAppObjectArgs(selection[0].getObject(),
4527
"setDriving(%d,%s)",
4530
finishDatumConstraint(this, Obj, false);
4533
finishDatumConstraint(this, Obj, true);
4538
else if (isCircleOrArc(*geom)) {
4539
auto [radius, center] = getRadiusCenterCircleArc(geom);
4540
Base::Vector3d d = center - pnt;
4541
double ActDist = std::abs(d.Length() - radius);
4543
openCommand(QT_TRANSLATE_NOOP("Command", "Add point to circle Distance constraint"));
4544
Gui::cmdAppObjectArgs(selection[0].getObject(),
4545
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%f))",
4547
static_cast<int>(PosId1),
4551
if (arebothpointsorsegmentsfixed
4552
|| constraintCreationMode
4554
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
4556
Gui::cmdAppObjectArgs(selection[0].getObject(),
4557
"setDriving(%d,%s)",
4560
finishDatumConstraint(this, Obj, false);
4563
finishDatumConstraint(this, Obj, true);
4569
else if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2)) {
4570
const Part::Geometry* geom1 = Obj->getGeometry(GeoId1);
4571
const Part::Geometry* geom2 = Obj->getGeometry(GeoId2);
4573
if(isCircleOrArc(*geom1) && isCircleOrArc(*geom2)) {
4575
auto [radius1, center1] = getRadiusCenterCircleArc(geom1);
4576
auto [radius2, center2] = getRadiusCenterCircleArc(geom2);
4578
double ActDist = 0.0;
4580
Base::Vector3d intercenter = center1 - center2;
4581
double intercenterdistance = intercenter.Length();
4583
if (intercenterdistance >= radius1 && intercenterdistance >= radius2) {
4585
ActDist = intercenterdistance - radius1 - radius2;
4588
double bigradius = std::max(radius1, radius2);
4589
double smallradius = std::min(radius1, radius2);
4591
ActDist = bigradius - smallradius - intercenterdistance;
4594
openCommand(QT_TRANSLATE_NOOP("Command", "Add circle to circle distance constraint"));
4595
Gui::cmdAppObjectArgs(selection[0].getObject(),
4596
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%f))",
4601
if (arebothpointsorsegmentsfixed
4602
|| constraintCreationMode
4604
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
4606
Gui::cmdAppObjectArgs(selection[0].getObject(),
4607
"setDriving(%d,%s)",
4610
finishDatumConstraint(this, Obj, false);
4613
finishDatumConstraint(this, Obj, true);
4618
else if ((isCircleOrArc(*geom1) && isLineSegment(*geom2))
4619
|| (isLineSegment(*geom1) && isCircleOrArc(*geom2))) {
4621
if (isLineSegment(*geom1)) {
4622
std::swap(geom1, geom2);
4623
std::swap(GeoId1, GeoId2);
4626
auto [radius, center] = getRadiusCenterCircleArc(geom1);
4628
auto lineSeg = static_cast<const Part::GeomLineSegment*>(geom2);
4629
Base::Vector3d pnt1 = lineSeg->getStartPoint();
4630
Base::Vector3d pnt2 = lineSeg->getEndPoint();
4631
Base::Vector3d d = pnt2 - pnt1;
4633
std::abs(-center.x * d.y + center.y * d.x + pnt1.x * pnt2.y - pnt2.x * pnt1.y)
4637
openCommand(QT_TRANSLATE_NOOP("Command", "Add circle to line distance constraint"));
4638
Gui::cmdAppObjectArgs(selection[0].getObject(),
4639
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%f)) ",
4644
if (arebothpointsorsegmentsfixed
4645
|| constraintCreationMode
4647
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
4649
Gui::cmdAppObjectArgs(selection[0].getObject(),
4650
"setDriving(%i,%s)",
4653
finishDatumConstraint(this, Obj, false);
4656
finishDatumConstraint(this, Obj, true);
4662
Gui::TranslatedNotification(
4664
QObject::tr("Wrong selection"),
4665
QObject::tr("Cannot add a length constraint on this selection!"));
4669
else if (isEdge(GeoId1, PosId1)) {
4670
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
4671
Gui::TranslatedUserWarning(Obj,
4672
QObject::tr("Wrong selection"),
4673
QObject::tr("Cannot add a length constraint on an axis!"));
4677
arebothpointsorsegmentsfixed = isPointOrSegmentFixed(Obj, GeoId1);
4679
const Part::Geometry* geom = Obj->getGeometry(GeoId1);
4681
if (isLineSegment(*geom)) {
4682
auto lineSeg = static_cast<const Part::GeomLineSegment*>(geom);
4683
double ActLength = (lineSeg->getEndPoint() - lineSeg->getStartPoint()).Length();
4685
openCommand(QT_TRANSLATE_NOOP("Command", "Add length constraint"));
4686
Gui::cmdAppObjectArgs(selection[0].getObject(),
4687
"addConstraint(Sketcher.Constraint('Distance',%d,%f))",
4692
if (arebothpointsorsegmentsfixed || GeoId1 <= Sketcher::GeoEnum::RefExt
4693
|| constraintCreationMode == Reference) {
4694
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
4696
Gui::cmdAppObjectArgs(selection[0].getObject(),
4697
"setDriving(%d,%s)",
4700
finishDatumConstraint(this, Obj, false);
4703
finishDatumConstraint(this, Obj, true);
4708
else if (isArcOfCircle(*geom)) {
4709
auto arc = static_cast<const Part::GeomArcOfCircle*>(geom);
4710
double ActLength = arc->getAngle(false) * arc->getRadius();
4712
openCommand(QT_TRANSLATE_NOOP("Command", "Add length constraint"));
4713
Gui::cmdAppObjectArgs(selection[0].getObject(),
4714
"addConstraint(Sketcher.Constraint('Distance',%d,%f))",
4719
if (arebothpointsorsegmentsfixed || GeoId1 <= Sketcher::GeoEnum::RefExt
4720
|| constraintCreationMode == Reference) {
4721
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
4723
Gui::cmdAppObjectArgs(selection[0].getObject(),
4724
"setDriving(%d,%s)",
4727
finishDatumConstraint(this, Obj, false);
4730
finishDatumConstraint(this, Obj, true);
4737
Gui::TranslatedUserWarning(Obj,
4738
QObject::tr("Wrong selection"),
4739
QObject::tr("Select exactly one line or one point and one line or "
4740
"two points or two circles from the sketch."));
4744
void CmdSketcherConstrainDistance::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
4746
SketcherGui::ViewProviderSketch* sketchgui =
4747
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
4748
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
4750
int GeoId1 = GeoEnum::GeoUndef, GeoId2 = GeoEnum::GeoUndef;
4751
Sketcher::PointPos PosId1 = Sketcher::PointPos::none, PosId2 = Sketcher::PointPos::none;
4753
bool arebothpointsorsegmentsfixed = areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2);
4759
GeoId1 = selSeq.at(0).GeoId;
4760
GeoId2 = selSeq.at(1).GeoId;
4761
PosId1 = selSeq.at(0).PosId;
4762
PosId2 = selSeq.at(1).PosId;
4764
Base::Vector3d pnt2 = Obj->getPoint(GeoId2, PosId2);
4766
if (GeoId1 == Sketcher::GeoEnum::HAxis && PosId1 == Sketcher::PointPos::none) {
4767
PosId1 = Sketcher::PointPos::start;
4770
QT_TRANSLATE_NOOP("Command", "Add distance from horizontal axis constraint"));
4771
Gui::cmdAppObjectArgs(
4773
"addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%d,%d,%f))",
4775
static_cast<int>(PosId1),
4777
static_cast<int>(PosId2),
4780
else if (GeoId1 == Sketcher::GeoEnum::VAxis && PosId1 == Sketcher::PointPos::none) {
4781
PosId1 = Sketcher::PointPos::start;
4784
QT_TRANSLATE_NOOP("Command", "Add distance from vertical axis constraint"));
4785
Gui::cmdAppObjectArgs(
4787
"addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%d,%d,%f))",
4789
static_cast<int>(PosId1),
4791
static_cast<int>(PosId2),
4795
Base::Vector3d pnt1 = Obj->getPoint(GeoId1, PosId1);
4797
openCommand(QT_TRANSLATE_NOOP("Command", "Add point to point distance constraint"));
4798
Gui::cmdAppObjectArgs(
4800
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%d,%f))",
4802
static_cast<int>(PosId1),
4804
static_cast<int>(PosId2),
4805
(pnt2 - pnt1).Length());
4808
if (arebothpointsorsegmentsfixed || constraintCreationMode == Reference) {
4810
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
4812
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
4813
finishDatumConstraint(this, Obj, false);
4816
finishDatumConstraint(this, Obj, true);
4824
GeoId1 = selSeq.at(0).GeoId;
4826
arebothpointsorsegmentsfixed = isPointOrSegmentFixed(Obj, GeoId1);
4828
const Part::Geometry* geom = Obj->getGeometry(GeoId1);
4830
if (isLineSegment(*geom)) {
4831
auto lineSeg = static_cast<const Part::GeomLineSegment*>(geom);
4832
double ActLength = (lineSeg->getEndPoint() - lineSeg->getStartPoint()).Length();
4834
openCommand(QT_TRANSLATE_NOOP("Command", "Add length constraint"));
4835
Gui::cmdAppObjectArgs(Obj,
4836
"addConstraint(Sketcher.Constraint('Distance',%d,%f))",
4840
if (arebothpointsorsegmentsfixed || GeoId1 <= Sketcher::GeoEnum::RefExt
4841
|| constraintCreationMode == Reference) {
4843
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
4845
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
4846
finishDatumConstraint(this, Obj, false);
4849
finishDatumConstraint(this, Obj, true);
4852
else if (isCircle(*geom)) {
4856
Gui::TranslatedUserWarning(
4858
QObject::tr("Wrong selection"),
4859
QObject::tr("This constraint does not make sense for non-linear curves."));
4869
GeoId1 = selSeq.at(0).GeoId;
4870
GeoId2 = selSeq.at(1).GeoId;
4871
PosId1 = selSeq.at(0).PosId;
4873
Base::Vector3d pnt = Obj->getPoint(GeoId1, PosId1);
4874
const Part::Geometry* geom = Obj->getGeometry(GeoId2);
4876
if (isLineSegment(*geom)) {
4877
auto lineSeg = static_cast<const Part::GeomLineSegment*>(geom);
4878
Base::Vector3d pnt1 = lineSeg->getStartPoint();
4879
Base::Vector3d pnt2 = lineSeg->getEndPoint();
4880
Base::Vector3d d = pnt2 - pnt1;
4882
std::abs(-pnt.x * d.y + pnt.y * d.x + pnt1.x * pnt2.y - pnt2.x * pnt1.y)
4885
openCommand(QT_TRANSLATE_NOOP("Command", "Add point to line Distance constraint"));
4886
Gui::cmdAppObjectArgs(Obj,
4887
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%f))",
4889
static_cast<int>(PosId1),
4893
if (arebothpointsorsegmentsfixed || constraintCreationMode == Reference) {
4895
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
4897
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
4898
finishDatumConstraint(this, Obj, false);
4901
finishDatumConstraint(this, Obj, true);
4909
GeoId1 = selSeq.at(0).GeoId;
4910
GeoId2 = selSeq.at(1).GeoId;
4911
const Part::Geometry* geom1 = Obj->getGeometry(GeoId1);
4912
const Part::Geometry* geom2 = Obj->getGeometry(GeoId2);
4914
if (isCircle(*geom1) && isCircle(*geom2)) {
4915
auto circleSeg1 = static_cast<const Part::GeomCircle*>(geom1);
4916
double radius1 = circleSeg1->getRadius();
4917
Base::Vector3d center1 = circleSeg1->getCenter();
4919
auto circleSeg2 = static_cast<const Part::GeomCircle*>(geom2);
4920
double radius2 = circleSeg2->getRadius();
4921
Base::Vector3d center2 = circleSeg2->getCenter();
4923
double ActDist = 0.;
4925
Base::Vector3d intercenter = center1 - center2;
4926
double intercenterdistance = intercenter.Length();
4928
if (intercenterdistance >= radius1 && intercenterdistance >= radius2) {
4930
ActDist = intercenterdistance - radius1 - radius2;
4933
double bigradius = std::max(radius1, radius2);
4934
double smallradius = std::min(radius1, radius2);
4936
ActDist = bigradius - smallradius - intercenterdistance;
4940
QT_TRANSLATE_NOOP("Command", "Add circle to circle distance constraint"));
4941
Gui::cmdAppObjectArgs(Obj,
4942
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%f))",
4947
if (arebothpointsorsegmentsfixed
4948
|| constraintCreationMode
4950
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
4952
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
4953
finishDatumConstraint(this, Obj, false);
4956
finishDatumConstraint(this, Obj, true);
4962
Gui::TranslatedUserWarning(
4964
QObject::tr("Wrong selection"),
4965
QObject::tr("Select exactly one line or one point and one line or two points "
4966
"or two circles from the sketch."));
4974
void CmdSketcherConstrainDistance::updateAction(int mode)
4979
getAction()->setIcon(
4980
Gui::BitmapFactory().iconFromTheme("Constraint_Length_Driven"));
4985
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Length"));
4993
class CmdSketcherConstrainDistanceX: public CmdSketcherConstraint
4996
CmdSketcherConstrainDistanceX();
4997
~CmdSketcherConstrainDistanceX() override
4999
void updateAction(int mode) override;
5000
const char* className() const override
5002
return "CmdSketcherConstrainDistanceX";
5006
void activated(int iMsg) override;
5007
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
5010
CmdSketcherConstrainDistanceX::CmdSketcherConstrainDistanceX()
5011
: CmdSketcherConstraint("Sketcher_ConstrainDistanceX")
5013
sAppModule = "Sketcher";
5014
sGroup = "Sketcher";
5015
sMenuText = QT_TR_NOOP("Constrain horizontal distance");
5016
sToolTipText = QT_TR_NOOP("Fix the horizontal distance "
5017
"between two points or line ends");
5018
sWhatsThis = "Sketcher_ConstrainDistanceX";
5019
sStatusTip = sToolTipText;
5020
sPixmap = "Constraint_HorizontalDistance";
5025
allowedSelSequences = {{SelVertex, SelVertexOrRoot},
5026
{SelRoot, SelVertex},
5031
void CmdSketcherConstrainDistanceX::activated(int iMsg)
5035
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
5038
if (selection.size() != 1
5039
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
5040
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
5041
"User parameter:BaseApp/Preferences/Mod/Sketcher");
5042
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
5044
if (constraintMode) {
5045
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
5046
getSelection().clearSelection();
5050
Gui::TranslatedUserWarning(getActiveGuiDocument(),
5051
QObject::tr("Wrong selection"),
5052
QObject::tr("Select the right things from the sketch."));
5058
const std::vector<std::string>& SubNames = selection[0].getSubNames();
5059
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
5061
if (SubNames.empty() || SubNames.size() > 2) {
5062
Gui::TranslatedUserWarning(
5064
QObject::tr("Wrong selection"),
5065
QObject::tr("Select exactly one line or up to two points from the sketch."));
5069
int GeoId1, GeoId2 = GeoEnum::GeoUndef;
5070
Sketcher::PointPos PosId1, PosId2 = Sketcher::PointPos::none;
5071
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
5072
if (SubNames.size() == 2) {
5073
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
5076
bool arebothpointsorsegmentsfixed = areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2);
5078
if (GeoId2 == Sketcher::GeoEnum::HAxis || GeoId2 == Sketcher::GeoEnum::VAxis) {
5079
std::swap(GeoId1, GeoId2);
5080
std::swap(PosId1, PosId2);
5083
if (GeoId1 == Sketcher::GeoEnum::HAxis && PosId1 == Sketcher::PointPos::none) {
5085
GeoId1 = GeoEnum::GeoUndef;
5087
else if (GeoId1 == Sketcher::GeoEnum::VAxis && PosId1 == Sketcher::PointPos::none) {
5088
GeoId1 = Sketcher::GeoEnum::HAxis;
5089
PosId1 = Sketcher::PointPos::start;
5092
if (isEdge(GeoId1, PosId1) && GeoId2 == GeoEnum::GeoUndef) {
5094
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
5095
Gui::TranslatedUserWarning(
5097
QObject::tr("Wrong selection"),
5098
QObject::tr("Cannot add a horizontal length constraint on an axis!"));
5102
arebothpointsorsegmentsfixed = isPointOrSegmentFixed(Obj, GeoId1);
5104
const Part::Geometry* geom = Obj->getGeometry(GeoId1);
5106
if (isLineSegment(*geom)) {
5108
PosId1 = Sketcher::PointPos::start;
5110
PosId2 = Sketcher::PointPos::end;
5113
if (isVertex(GeoId1, PosId1) && isVertex(GeoId2, PosId2)) {
5115
Base::Vector3d pnt1 = Obj->getPoint(GeoId1, PosId1);
5116
Base::Vector3d pnt2 = Obj->getPoint(GeoId2, PosId2);
5117
double ActLength = pnt2.x - pnt1.x;
5120
if (ActLength < -Precision::Confusion()) {
5121
std::swap(GeoId1, GeoId2);
5122
std::swap(PosId1, PosId2);
5123
std::swap(pnt1, pnt2);
5124
ActLength = -ActLength;
5128
QT_TRANSLATE_NOOP("Command", "Add point to point horizontal distance constraint"));
5129
Gui::cmdAppObjectArgs(selection[0].getObject(),
5130
"addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%d,%d,%f))",
5132
static_cast<int>(PosId1),
5134
static_cast<int>(PosId2),
5137
if (arebothpointsorsegmentsfixed || constraintCreationMode == Reference) {
5139
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
5141
Gui::cmdAppObjectArgs(selection[0].getObject(),
5142
"setDriving(%d,%s)",
5145
finishDatumConstraint(this, Obj, false);
5148
finishDatumConstraint(this, Obj, true);
5153
else if (isVertex(GeoId1, PosId1) && GeoId2 == GeoEnum::GeoUndef) {
5156
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
5157
Gui::TranslatedUserWarning(
5159
QObject::tr("Wrong selection"),
5160
QObject::tr("Cannot add a fixed x-coordinate constraint on the origin point!"));
5164
Base::Vector3d pnt = Obj->getPoint(GeoId1, PosId1);
5165
double ActX = pnt.x;
5167
arebothpointsorsegmentsfixed = isPointOrSegmentFixed(Obj, GeoId1);
5169
openCommand(QT_TRANSLATE_NOOP("Command", "Add fixed x-coordinate constraint"));
5170
Gui::cmdAppObjectArgs(selection[0].getObject(),
5171
"addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%f))",
5173
static_cast<int>(PosId1),
5176
if (arebothpointsorsegmentsfixed || constraintCreationMode == Reference) {
5178
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
5180
Gui::cmdAppObjectArgs(selection[0].getObject(),
5181
"setDriving(%d,%s)",
5184
finishDatumConstraint(this, Obj, false);
5187
finishDatumConstraint(this, Obj, true);
5193
Gui::TranslatedUserWarning(
5195
QObject::tr("Wrong selection"),
5196
QObject::tr("Select exactly one line or up to two points from the sketch."));
5200
void CmdSketcherConstrainDistanceX::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
5202
SketcherGui::ViewProviderSketch* sketchgui =
5203
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
5204
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
5206
int GeoId1 = GeoEnum::GeoUndef, GeoId2 = GeoEnum::GeoUndef;
5207
Sketcher::PointPos PosId1 = Sketcher::PointPos::none, PosId2 = Sketcher::PointPos::none;
5213
GeoId1 = selSeq.at(0).GeoId;
5214
GeoId2 = selSeq.at(1).GeoId;
5215
PosId1 = selSeq.at(0).PosId;
5216
PosId2 = selSeq.at(1).PosId;
5222
GeoId1 = GeoId2 = selSeq.at(0).GeoId;
5223
PosId1 = Sketcher::PointPos::start;
5224
PosId2 = Sketcher::PointPos::end;
5226
const Part::Geometry* geom = Obj->getGeometry(GeoId1);
5228
if (! isLineSegment(*geom)) {
5229
Gui::TranslatedUserWarning(
5231
QObject::tr("Wrong selection"),
5233
"This constraint only makes sense on a line segment or a pair of points."));
5243
Base::Vector3d pnt1 = Obj->getPoint(GeoId1, PosId1);
5244
Base::Vector3d pnt2 = Obj->getPoint(GeoId2, PosId2);
5245
double ActLength = pnt2.x - pnt1.x;
5248
if (ActLength < -Precision::Confusion()) {
5249
std::swap(GeoId1, GeoId2);
5250
std::swap(PosId1, PosId2);
5251
std::swap(pnt1, pnt2);
5252
ActLength = -ActLength;
5255
openCommand(QT_TRANSLATE_NOOP("Command", "Add point to point horizontal distance constraint"));
5256
Gui::cmdAppObjectArgs(Obj,
5257
"addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%d,%d,%f))",
5259
static_cast<int>(PosId1),
5261
static_cast<int>(PosId2),
5264
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2) || constraintCreationMode == Reference) {
5266
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
5268
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
5269
finishDatumConstraint(this, Obj, false);
5272
finishDatumConstraint(this, Obj, true);
5276
void CmdSketcherConstrainDistanceX::updateAction(int mode)
5281
getAction()->setIcon(
5282
Gui::BitmapFactory().iconFromTheme("Constraint_HorizontalDistance_Driven"));
5287
getAction()->setIcon(
5288
Gui::BitmapFactory().iconFromTheme("Constraint_HorizontalDistance"));
5297
class CmdSketcherConstrainDistanceY: public CmdSketcherConstraint
5300
CmdSketcherConstrainDistanceY();
5301
~CmdSketcherConstrainDistanceY() override
5303
void updateAction(int mode) override;
5304
const char* className() const override
5306
return "CmdSketcherConstrainDistanceY";
5310
void activated(int iMsg) override;
5311
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
5314
CmdSketcherConstrainDistanceY::CmdSketcherConstrainDistanceY()
5315
: CmdSketcherConstraint("Sketcher_ConstrainDistanceY")
5317
sAppModule = "Sketcher";
5318
sGroup = "Sketcher";
5319
sMenuText = QT_TR_NOOP("Constrain vertical distance");
5320
sToolTipText = QT_TR_NOOP("Fix the vertical distance between two points or line ends");
5321
sWhatsThis = "Sketcher_ConstrainDistanceY";
5322
sStatusTip = sToolTipText;
5323
sPixmap = "Constraint_VerticalDistance";
5328
allowedSelSequences = {{SelVertex, SelVertexOrRoot},
5329
{SelRoot, SelVertex},
5334
void CmdSketcherConstrainDistanceY::activated(int iMsg)
5338
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
5341
if (selection.size() != 1
5342
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
5343
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
5344
"User parameter:BaseApp/Preferences/Mod/Sketcher");
5345
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
5347
if (constraintMode) {
5348
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
5349
getSelection().clearSelection();
5353
Gui::TranslatedUserWarning(getActiveGuiDocument(),
5354
QObject::tr("Wrong selection"),
5355
QObject::tr("Select the right things from the sketch."));
5361
const std::vector<std::string>& SubNames = selection[0].getSubNames();
5362
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
5364
if (SubNames.empty() || SubNames.size() > 2) {
5365
Gui::TranslatedUserWarning(
5367
QObject::tr("Wrong selection"),
5368
QObject::tr("Select exactly one line or up to two points from the sketch."));
5372
int GeoId1, GeoId2 = GeoEnum::GeoUndef;
5373
Sketcher::PointPos PosId1, PosId2 = Sketcher::PointPos::none;
5374
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
5375
if (SubNames.size() == 2) {
5376
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
5379
bool arebothpointsorsegmentsfixed = areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2);
5381
if (GeoId2 == Sketcher::GeoEnum::HAxis || GeoId2 == Sketcher::GeoEnum::VAxis) {
5382
std::swap(GeoId1, GeoId2);
5383
std::swap(PosId1, PosId2);
5386
if (GeoId1 == Sketcher::GeoEnum::VAxis
5387
&& PosId1 == Sketcher::PointPos::none) {
5388
GeoId1 = GeoEnum::GeoUndef;
5390
else if (GeoId1 == Sketcher::GeoEnum::HAxis && PosId1 == Sketcher::PointPos::none) {
5391
PosId1 = Sketcher::PointPos::start;
5394
if (isEdge(GeoId1, PosId1) && GeoId2 == GeoEnum::GeoUndef) {
5395
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
5396
Gui::TranslatedUserWarning(
5398
QObject::tr("Wrong selection"),
5399
QObject::tr("Cannot add a vertical length constraint on an axis!"));
5403
arebothpointsorsegmentsfixed = isPointOrSegmentFixed(Obj, GeoId1);
5405
const Part::Geometry* geom = Obj->getGeometry(GeoId1);
5407
if (isLineSegment(*geom)) {
5409
PosId1 = Sketcher::PointPos::start;
5411
PosId2 = Sketcher::PointPos::end;
5415
if (isVertex(GeoId1, PosId1) && isVertex(GeoId2, PosId2)) {
5417
Base::Vector3d pnt1 = Obj->getPoint(GeoId1, PosId1);
5418
Base::Vector3d pnt2 = Obj->getPoint(GeoId2, PosId2);
5419
double ActLength = pnt2.y - pnt1.y;
5422
if (ActLength < -Precision::Confusion()) {
5423
std::swap(GeoId1, GeoId2);
5424
std::swap(PosId1, PosId2);
5425
std::swap(pnt1, pnt2);
5426
ActLength = -ActLength;
5430
QT_TRANSLATE_NOOP("Command", "Add point to point vertical distance constraint"));
5431
Gui::cmdAppObjectArgs(selection[0].getObject(),
5432
"addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%d,%d,%f))",
5434
static_cast<int>(PosId1),
5436
static_cast<int>(PosId2),
5439
if (arebothpointsorsegmentsfixed || constraintCreationMode == Reference) {
5441
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
5443
Gui::cmdAppObjectArgs(selection[0].getObject(),
5444
"setDriving(%d,%s)",
5447
finishDatumConstraint(this, Obj, false);
5450
finishDatumConstraint(this, Obj, true);
5455
else if (isVertex(GeoId1, PosId1) && GeoId2 == GeoEnum::GeoUndef) {
5457
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
5458
Gui::TranslatedUserWarning(
5460
QObject::tr("Wrong selection"),
5461
QObject::tr("Cannot add a fixed y-coordinate constraint on the origin point!"));
5465
Base::Vector3d pnt = Obj->getPoint(GeoId1, PosId1);
5466
double ActY = pnt.y;
5468
openCommand(QT_TRANSLATE_NOOP("Command", "Add fixed y-coordinate constraint"));
5469
Gui::cmdAppObjectArgs(selection[0].getObject(),
5470
"addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%f))",
5472
static_cast<int>(PosId1),
5475
if (GeoId1 <= Sketcher::GeoEnum::RefExt || constraintCreationMode == Reference) {
5477
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
5479
Gui::cmdAppObjectArgs(selection[0].getObject(),
5480
"setDriving(%d,%s)",
5483
finishDatumConstraint(this, Obj, false);
5486
finishDatumConstraint(this, Obj, true);
5492
Gui::TranslatedUserWarning(
5494
QObject::tr("Wrong selection"),
5495
QObject::tr("Select exactly one line or up to two points from the sketch."));
5499
void CmdSketcherConstrainDistanceY::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
5501
SketcherGui::ViewProviderSketch* sketchgui =
5502
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
5503
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
5505
int GeoId1 = GeoEnum::GeoUndef, GeoId2 = GeoEnum::GeoUndef;
5506
Sketcher::PointPos PosId1 = Sketcher::PointPos::none, PosId2 = Sketcher::PointPos::none;
5512
GeoId1 = selSeq.at(0).GeoId;
5513
GeoId2 = selSeq.at(1).GeoId;
5514
PosId1 = selSeq.at(0).PosId;
5515
PosId2 = selSeq.at(1).PosId;
5521
GeoId1 = GeoId2 = selSeq.at(0).GeoId;
5522
PosId1 = Sketcher::PointPos::start;
5523
PosId2 = Sketcher::PointPos::end;
5525
const Part::Geometry* geom = Obj->getGeometry(GeoId1);
5527
if (! isLineSegment(*geom)) {
5528
Gui::TranslatedUserWarning(
5530
QObject::tr("Wrong selection"),
5532
"This constraint only makes sense on a line segment or a pair of points."));
5542
Base::Vector3d pnt1 = Obj->getPoint(GeoId1, PosId1);
5543
Base::Vector3d pnt2 = Obj->getPoint(GeoId2, PosId2);
5544
double ActLength = pnt2.y - pnt1.y;
5547
if (ActLength < -Precision::Confusion()) {
5548
std::swap(GeoId1, GeoId2);
5549
std::swap(PosId1, PosId2);
5550
std::swap(pnt1, pnt2);
5551
ActLength = -ActLength;
5554
openCommand(QT_TRANSLATE_NOOP("Command", "Add point to point vertical distance constraint"));
5555
Gui::cmdAppObjectArgs(Obj,
5556
"addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%d,%d,%f))",
5558
static_cast<int>(PosId1),
5560
static_cast<int>(PosId2),
5563
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)
5564
|| constraintCreationMode
5566
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
5568
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
5569
finishDatumConstraint(this, Obj, false);
5572
finishDatumConstraint(this, Obj, true);
5576
void CmdSketcherConstrainDistanceY::updateAction(int mode)
5581
getAction()->setIcon(
5582
Gui::BitmapFactory().iconFromTheme("Constraint_VerticalDistance_Driven"));
5587
getAction()->setIcon(
5588
Gui::BitmapFactory().iconFromTheme("Constraint_VerticalDistance"));
5596
class CmdSketcherConstrainParallel: public CmdSketcherConstraint
5599
CmdSketcherConstrainParallel();
5600
~CmdSketcherConstrainParallel() override
5602
const char* className() const override
5604
return "CmdSketcherConstrainParallel";
5608
void activated(int iMsg) override;
5609
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
5612
CmdSketcherConstrainParallel::CmdSketcherConstrainParallel()
5613
: CmdSketcherConstraint("Sketcher_ConstrainParallel")
5615
sAppModule = "Sketcher";
5616
sGroup = "Sketcher";
5617
sMenuText = QT_TR_NOOP("Constrain parallel");
5618
sToolTipText = QT_TR_NOOP("Create a parallel constraint between two lines");
5619
sWhatsThis = "Sketcher_ConstrainParallel";
5620
sStatusTip = sToolTipText;
5621
sPixmap = "Constraint_Parallel";
5626
allowedSelSequences = {{SelEdge, SelEdgeOrAxis},
5627
{SelEdgeOrAxis, SelEdge},
5628
{SelEdge, SelExternalEdge},
5629
{SelExternalEdge, SelEdge}};
5632
void CmdSketcherConstrainParallel::activated(int iMsg)
5636
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
5639
if (selection.size() != 1
5640
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
5641
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
5642
"User parameter:BaseApp/Preferences/Mod/Sketcher");
5643
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
5645
if (constraintMode) {
5646
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
5647
getSelection().clearSelection();
5651
Gui::TranslatedUserWarning(getActiveGuiDocument(),
5652
QObject::tr("Wrong selection"),
5653
QObject::tr("Select two or more lines from the sketch."));
5658
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
5661
std::vector<int> ids;
5662
bool hasAlreadyExternal = false;
5663
for (auto& subname : selection[0].getSubNames()) {
5666
Sketcher::PointPos PosId;
5667
getIdsFromName(subname, Obj, GeoId, PosId);
5669
if (!isEdge(GeoId, PosId)) {
5672
else if (isPointOrSegmentFixed(Obj, GeoId)) {
5673
if (hasAlreadyExternal) {
5674
showNoConstraintBetweenFixedGeometry(Obj);
5678
hasAlreadyExternal = true;
5683
const Part::Geometry* geo = Obj->getGeometry(GeoId);
5685
if (!isLineSegment(*geo)) {
5686
Gui::TranslatedUserWarning(Obj,
5687
QObject::tr("Wrong selection"),
5688
QObject::tr("One selected edge is not a valid line."));
5691
ids.push_back(GeoId);
5694
if (ids.size() < 2) {
5695
Gui::TranslatedUserWarning(Obj,
5696
QObject::tr("Wrong selection"),
5697
QObject::tr("Select at least two lines from the sketch."));
5702
openCommand(QT_TRANSLATE_NOOP("Command", "Add parallel constraint"));
5703
for (int i = 0; i < int(ids.size() - 1); i++) {
5704
Gui::cmdAppObjectArgs(selection[0].getObject(),
5705
"addConstraint(Sketcher.Constraint('Parallel',%d,%d))",
5712
tryAutoRecompute(Obj);
5715
getSelection().clearSelection();
5718
void CmdSketcherConstrainParallel::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
5726
SketcherGui::ViewProviderSketch* sketchgui =
5727
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
5728
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
5730
int GeoId1 = selSeq.at(0).GeoId, GeoId2 = selSeq.at(1).GeoId;
5733
if (! isLineSegment(*(Obj->getGeometry(GeoId1))) || ! isLineSegment(*(Obj->getGeometry(GeoId2)))) {
5734
Gui::TranslatedUserWarning(Obj,
5735
QObject::tr("Wrong selection"),
5736
QObject::tr("The selected edge is not a valid line."));
5740
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
5741
showNoConstraintBetweenFixedGeometry(Obj);
5746
openCommand(QT_TRANSLATE_NOOP("Command", "Add parallel constraint"));
5747
Gui::cmdAppObjectArgs(sketchgui->getObject(),
5748
"addConstraint(Sketcher.Constraint('Parallel',%d,%d))",
5753
tryAutoRecompute(Obj);
5759
class CmdSketcherConstrainPerpendicular: public CmdSketcherConstraint
5762
CmdSketcherConstrainPerpendicular();
5763
~CmdSketcherConstrainPerpendicular() override
5765
const char* className() const override
5767
return "CmdSketcherConstrainPerpendicular";
5771
void activated(int iMsg) override;
5772
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
5775
CmdSketcherConstrainPerpendicular::CmdSketcherConstrainPerpendicular()
5776
: CmdSketcherConstraint("Sketcher_ConstrainPerpendicular")
5778
sAppModule = "Sketcher";
5779
sGroup = "Sketcher";
5780
sMenuText = QT_TR_NOOP("Constrain perpendicular");
5781
sToolTipText = QT_TR_NOOP("Create a perpendicular constraint between two lines");
5782
sWhatsThis = "Sketcher_ConstrainPerpendicular";
5783
sStatusTip = sToolTipText;
5784
sPixmap = "Constraint_Perpendicular";
5789
allowedSelSequences = {{SelEdge, SelEdgeOrAxis},
5790
{SelEdgeOrAxis, SelEdge},
5791
{SelEdge, SelExternalEdge},
5792
{SelExternalEdge, SelEdge},
5793
{SelVertexOrRoot, SelEdge, SelEdgeOrAxis},
5794
{SelVertexOrRoot, SelEdgeOrAxis, SelEdge},
5795
{SelVertexOrRoot, SelEdge, SelExternalEdge},
5796
{SelVertexOrRoot, SelExternalEdge, SelEdge},
5797
{SelEdge, SelVertexOrRoot, SelEdgeOrAxis},
5798
{SelEdgeOrAxis, SelVertexOrRoot, SelEdge},
5799
{SelEdge, SelVertexOrRoot, SelExternalEdge},
5800
{SelExternalEdge, SelVertexOrRoot, SelEdge}};
5804
void CmdSketcherConstrainPerpendicular::activated(int iMsg)
5809
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
5812
if (selection.size() != 1
5813
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
5814
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
5815
"User parameter:BaseApp/Preferences/Mod/Sketcher");
5816
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
5818
if (constraintMode) {
5819
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
5820
getSelection().clearSelection();
5824
QString strBasicHelp =
5825
QObject::tr("There is a number of ways this constraint can be applied.\n\n"
5826
"Accepted combinations: two curves; an endpoint and a curve; two "
5827
"endpoints; two curves and a point.",
5828
"perpendicular constraint");
5830
QObject::tr("Select some geometry from the sketch.", "perpendicular constraint");
5831
strError.append(QString::fromLatin1("\n\n"));
5832
strError.append(strBasicHelp);
5833
Gui::TranslatedUserWarning(getActiveGuiDocument(),
5834
QObject::tr("Wrong selection"),
5835
std::move(strError));
5841
const std::vector<std::string>& SubNames = selection[0].getSubNames();
5842
auto* Obj = dynamic_cast<Sketcher::SketchObject*>(selection[0].getObject());
5844
if (!Obj || (SubNames.size() != 2 && SubNames.size() != 3)) {
5845
Gui::TranslatedUserWarning(Obj,
5846
QObject::tr("Wrong selection"),
5847
QObject::tr("Wrong number of selected objects!"));
5851
int GeoId1, GeoId2, GeoId3;
5852
Sketcher::PointPos PosId1, PosId2, PosId3;
5853
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
5854
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
5856
if (areBothPointsOrSegmentsFixed(Obj,
5859
showNoConstraintBetweenFixedGeometry(Obj);
5863
if (SubNames.size() == 3) {
5864
getIdsFromName(SubNames[2], Obj, GeoId3, PosId3);
5867
if (isVertex(GeoId1, PosId1)) {
5868
std::swap(GeoId1, GeoId2);
5869
std::swap(PosId1, PosId2);
5871
if (isVertex(GeoId2, PosId2)) {
5872
std::swap(GeoId2, GeoId3);
5873
std::swap(PosId2, PosId3);
5876
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
5878
if (isBsplinePole(Obj, GeoId1) || isBsplinePole(Obj, GeoId2)) {
5879
Gui::TranslatedUserWarning(
5881
QObject::tr("Wrong selection"),
5882
QObject::tr("Select an edge that is not a B-spline weight."));
5886
openCommand(QT_TRANSLATE_NOOP("Command", "Add perpendicular constraint"));
5888
bool safe = addConstraintSafely(Obj, [&]() {
5890
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {
5891
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
5892
if (!(geom1 && isBSplineCurve(*geom1))) {
5893
Gui::cmdAppObjectArgs(
5894
selection[0].getObject(),
5895
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
5897
static_cast<int>(PosId3),
5902
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
5903
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
5904
if (!(geom2 && isBSplineCurve(*geom2))) {
5905
Gui::cmdAppObjectArgs(
5906
selection[0].getObject(),
5907
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
5909
static_cast<int>(PosId3),
5914
if (!IsPointAlreadyOnCurve(
5920
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
5921
if (!(geom1 && isBSplineCurve(*geom1))) {
5922
Gui::cmdAppObjectArgs(
5923
selection[0].getObject(),
5924
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
5926
static_cast<int>(PosId3),
5931
Gui::cmdAppObjectArgs(
5932
selection[0].getObject(),
5933
"addConstraint(Sketcher.Constraint('PerpendicularViaPoint',%d,%d,%d,%d))",
5937
static_cast<int>(PosId3));
5939
removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3);
5947
tryAutoRecompute(Obj);
5950
getSelection().clearSelection();
5955
Gui::TranslatedUserWarning(
5957
QObject::tr("Wrong selection"),
5958
QObject::tr("With 3 objects, there must be 2 curves and 1 point."));
5960
else if (SubNames.size() == 2) {
5961
if (isVertex(GeoId1, PosId1)
5962
&& isVertex(GeoId2, PosId2)) {
5964
if (isSimpleVertex(Obj, GeoId1, PosId1) || isSimpleVertex(Obj, GeoId2, PosId2)) {
5965
Gui::TranslatedUserWarning(
5967
QObject::tr("Wrong selection"),
5969
"Cannot add a perpendicularity constraint at an unconnected point!"));
5974
const Part::Geometry* geom1 = Obj->getGeometry(GeoId1);
5975
const Part::Geometry* geom2 = Obj->getGeometry(GeoId2);
5977
if (geom1 && geom2 && (isBSplineCurve(*geom1) || isBSplineCurve(*geom2))) {
5978
if (! isBSplineCurve(*geom1)) {
5979
std::swap(GeoId1, GeoId2);
5980
std::swap(PosId1, PosId2);
5985
openCommand(QT_TRANSLATE_NOOP("Command", "Add perpendicular constraint"));
5986
Gui::cmdAppObjectArgs(selection[0].getObject(),
5987
"addConstraint(Sketcher.Constraint('Perpendicular',%d,%d,%d,%d))",
5989
static_cast<int>(PosId1),
5991
static_cast<int>(PosId2));
5993
tryAutoRecompute(Obj);
5995
getSelection().clearSelection();
5998
else if ((isVertex(GeoId1, PosId1) && isEdge(GeoId2, PosId2))
5999
|| (isEdge(GeoId1, PosId1) && isVertex(GeoId2, PosId2))) {
6000
if (isVertex(GeoId2, PosId2)) {
6001
std::swap(GeoId1, GeoId2);
6002
std::swap(PosId1, PosId2);
6005
if (isSimpleVertex(Obj, GeoId1, PosId1)) {
6006
Gui::TranslatedUserWarning(
6008
QObject::tr("Wrong selection"),
6010
"Cannot add a perpendicularity constraint at an unconnected point!"));
6014
const Part::Geometry* geom2 = Obj->getGeometry(GeoId2);
6016
if (isBsplinePole(geom2)) {
6017
Gui::TranslatedUserWarning(
6019
QObject::tr("Wrong selection"),
6020
QObject::tr("Select an edge that is not a B-spline weight."));
6024
openCommand(QT_TRANSLATE_NOOP("Command", "Add perpendicularity constraint"));
6025
Gui::cmdAppObjectArgs(selection[0].getObject(),
6026
"addConstraint(Sketcher.Constraint('Perpendicular',%d,%d,%d))",
6028
static_cast<int>(PosId1),
6031
tryAutoRecompute(Obj);
6033
getSelection().clearSelection();
6036
else if (isEdge(GeoId1, PosId1)
6037
&& isEdge(GeoId2, PosId2)) {
6039
const Part::Geometry* geo1 = Obj->getGeometry(GeoId1);
6040
const Part::Geometry* geo2 = Obj->getGeometry(GeoId2);
6041
if (!geo1 || !geo2) {
6045
if (! isLineSegment(*geo1) && ! isLineSegment(*geo2)) {
6046
Gui::TranslatedUserWarning(
6048
QObject::tr("Wrong selection"),
6049
QObject::tr("One of the selected edges should be a line."));
6062
if (isLineSegment(*geo1)) {
6063
std::swap(GeoId1, GeoId2);
6066
if (isBsplinePole(Obj, GeoId1)) {
6067
Gui::TranslatedUserWarning(
6069
QObject::tr("Wrong selection"),
6070
QObject::tr("Select an edge that is not a B-spline weight."));
6075
geo1 = Obj->getGeometry(GeoId1);
6076
geo2 = Obj->getGeometry(GeoId2);
6078
if (isEllipse(*geo1) || isArcOfEllipse(*geo1) || isArcOfHyperbola(*geo1) || isArcOfParabola(*geo1)) {
6079
Base::Vector3d center;
6080
Base::Vector3d majdir;
6081
Base::Vector3d focus;
6086
if (isEllipse(*geo1)) {
6087
auto ellipse = static_cast<const Part::GeomEllipse*>(geo1);
6088
center = ellipse->getCenter();
6089
majord = ellipse->getMajorRadius();
6090
minord = ellipse->getMinorRadius();
6091
majdir = ellipse->getMajorAxisDir();
6092
phi = atan2(majdir.y, majdir.x);
6094
else if (isArcOfEllipse(*geo1)) {
6095
auto aoe = static_cast<const Part::GeomArcOfEllipse*>(geo1);
6096
center = aoe->getCenter();
6097
majord = aoe->getMajorRadius();
6098
minord = aoe->getMinorRadius();
6099
majdir = aoe->getMajorAxisDir();
6100
phi = atan2(majdir.y, majdir.x);
6102
else if (isArcOfHyperbola(*geo1)) {
6103
auto aoh = static_cast<const Part::GeomArcOfHyperbola*>(geo1);
6104
center = aoh->getCenter();
6105
majord = aoh->getMajorRadius();
6106
minord = aoh->getMinorRadius();
6107
majdir = aoh->getMajorAxisDir();
6108
phi = atan2(majdir.y, majdir.x);
6110
else if (isArcOfParabola(*geo1)) {
6111
auto aop = static_cast<const Part::GeomArcOfParabola*>(geo1);
6112
center = aop->getCenter();
6113
focus = aop->getFocus();
6116
const Part::GeomLineSegment* line = static_cast<const Part::GeomLineSegment*>(geo2);
6118
Base::Vector3d point1 = line->getStartPoint();
6121
if (isArcOfHyperbola(*geo1)) {
6122
double df = sqrt(majord * majord + minord * minord);
6123
Base::Vector3d direction = point1 - (center + majdir * df);
6124
double tapprox = atan2(direction.y, direction.x) - phi;
6126
PoO = Base::Vector3d(center.x + majord * cosh(tapprox) * cos(phi)
6127
- minord * sinh(tapprox) * sin(phi),
6128
center.y + majord * cosh(tapprox) * sin(phi)
6129
+ minord * sinh(tapprox) * cos(phi),
6132
else if (isArcOfParabola(*geo1)) {
6133
Base::Vector3d direction = point1 - focus;
6135
PoO = point1 + direction / 2;
6138
Base::Vector3d direction = point1 - center;
6139
double tapprox = atan2(direction.y, direction.x)
6142
PoO = Base::Vector3d(center.x + majord * cos(tapprox) * cos(phi)
6143
- minord * sin(tapprox) * sin(phi),
6144
center.y + majord * cos(tapprox) * sin(phi)
6145
+ minord * sin(tapprox) * cos(phi),
6148
openCommand(QT_TRANSLATE_NOOP("Command", "Add perpendicular constraint"));
6152
Gui::cmdAppObjectArgs(Obj,
6153
"addGeometry(Part.Point(App.Vector(%f,%f,0)), True)",
6156
int GeoIdPoint = Obj->getHighestCurveIndex();
6159
Gui::cmdAppObjectArgs(
6160
selection[0].getObject(),
6161
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
6163
static_cast<int>(Sketcher::PointPos::start),
6166
Gui::cmdAppObjectArgs(
6167
selection[0].getObject(),
6168
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
6170
static_cast<int>(Sketcher::PointPos::start),
6173
Gui::cmdAppObjectArgs(
6175
"addConstraint(Sketcher.Constraint('PerpendicularViaPoint',%d,%d,%d,%d))",
6179
static_cast<int>(Sketcher::PointPos::start));
6181
catch (const Base::Exception& e) {
6182
Gui::NotifyUserError(Obj,
6183
QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
6185
Gui::Command::abortCommand();
6187
tryAutoRecompute(Obj);
6192
tryAutoRecompute(Obj);
6194
getSelection().clearSelection();
6198
openCommand(QT_TRANSLATE_NOOP("Command", "Add perpendicular constraint"));
6199
Gui::cmdAppObjectArgs(selection[0].getObject(),
6200
"addConstraint(Sketcher.Constraint('Perpendicular',%d,%d))",
6204
tryAutoRecompute(Obj);
6206
getSelection().clearSelection();
6214
void CmdSketcherConstrainPerpendicular::applyConstraint(std::vector<SelIdPair>& selSeq,
6217
SketcherGui::ViewProviderSketch* sketchgui =
6218
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
6219
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
6221
int GeoId1 = GeoEnum::GeoUndef, GeoId2 = GeoEnum::GeoUndef, GeoId3 = GeoEnum::GeoUndef;
6222
Sketcher::PointPos PosId1 = Sketcher::PointPos::none, PosId2 = Sketcher::PointPos::none,
6223
PosId3 = Sketcher::PointPos::none;
6231
GeoId1 = selSeq.at(0).GeoId;
6232
GeoId2 = selSeq.at(1).GeoId;
6235
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
6236
showNoConstraintBetweenFixedGeometry(Obj);
6240
const Part::Geometry* geo1 = Obj->getGeometry(GeoId1);
6241
const Part::Geometry* geo2 = Obj->getGeometry(GeoId2);
6242
if (!geo1 || !geo2) {
6246
if (! isLineSegment(*geo1) && ! isLineSegment(*geo2)) {
6247
Gui::TranslatedUserWarning(
6249
QObject::tr("Wrong selection"),
6250
QObject::tr("One of the selected edges should be a line."));
6263
if (isLineSegment(*geo1)) {
6264
std::swap(GeoId1, GeoId2);
6267
if (isBsplinePole(Obj, GeoId1)) {
6268
Gui::TranslatedUserWarning(
6270
QObject::tr("Wrong selection"),
6271
QObject::tr("Select an edge that is not a B-spline weight."));
6276
geo1 = Obj->getGeometry(GeoId1);
6277
geo2 = Obj->getGeometry(GeoId2);
6279
if (isEllipse(*geo1) || isArcOfEllipse(*geo1) || isArcOfHyperbola(*geo1) || isArcOfParabola(*geo1)) {
6280
Base::Vector3d center;
6281
Base::Vector3d majdir;
6282
Base::Vector3d focus;
6287
if (isEllipse(*geo1)) {
6288
auto ellipse = static_cast<const Part::GeomEllipse*>(geo1);
6289
center = ellipse->getCenter();
6290
majord = ellipse->getMajorRadius();
6291
minord = ellipse->getMinorRadius();
6292
majdir = ellipse->getMajorAxisDir();
6293
phi = atan2(majdir.y, majdir.x);
6295
else if (isArcOfEllipse(*geo1)) {
6296
auto aoe = static_cast<const Part::GeomArcOfEllipse*>(geo1);
6297
center = aoe->getCenter();
6298
majord = aoe->getMajorRadius();
6299
minord = aoe->getMinorRadius();
6300
majdir = aoe->getMajorAxisDir();
6301
phi = atan2(majdir.y, majdir.x);
6303
else if (isArcOfHyperbola(*geo1)) {
6304
auto aoh = static_cast<const Part::GeomArcOfHyperbola*>(geo1);
6305
center = aoh->getCenter();
6306
majord = aoh->getMajorRadius();
6307
minord = aoh->getMinorRadius();
6308
majdir = aoh->getMajorAxisDir();
6309
phi = atan2(majdir.y, majdir.x);
6311
else if (isArcOfParabola(*geo1)) {
6312
auto aop = static_cast<const Part::GeomArcOfParabola*>(geo1);
6313
center = aop->getCenter();
6314
focus = aop->getFocus();
6317
const Part::GeomLineSegment* line = static_cast<const Part::GeomLineSegment*>(geo2);
6319
Base::Vector3d point1 = line->getStartPoint();
6322
if (isArcOfHyperbola(*geo1)) {
6323
double df = sqrt(majord * majord + minord * minord);
6324
Base::Vector3d direction = point1 - (center + majdir * df);
6325
double tapprox = atan2(direction.y, direction.x) - phi;
6327
PoO = Base::Vector3d(center.x + majord * cosh(tapprox) * cos(phi)
6328
- minord * sinh(tapprox) * sin(phi),
6329
center.y + majord * cosh(tapprox) * sin(phi)
6330
+ minord * sinh(tapprox) * cos(phi),
6333
else if (isArcOfParabola(*geo1)) {
6334
Base::Vector3d direction = point1 - focus;
6336
PoO = point1 + direction / 2;
6339
Base::Vector3d direction = point1 - center;
6340
double tapprox = atan2(direction.y, direction.x)
6343
PoO = Base::Vector3d(center.x + majord * cos(tapprox) * cos(phi)
6344
- minord * sin(tapprox) * sin(phi),
6345
center.y + majord * cos(tapprox) * sin(phi)
6346
+ minord * sin(tapprox) * cos(phi),
6349
openCommand(QT_TRANSLATE_NOOP("Command", "Add perpendicular constraint"));
6353
Gui::cmdAppObjectArgs(Obj,
6354
"addGeometry(Part.Point(App.Vector(%f,%f,0)), True)",
6357
int GeoIdPoint = Obj->getHighestCurveIndex();
6360
Gui::cmdAppObjectArgs(
6362
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
6364
static_cast<int>(Sketcher::PointPos::start),
6367
Gui::cmdAppObjectArgs(
6369
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
6371
static_cast<int>(Sketcher::PointPos::start),
6375
Gui::cmdAppObjectArgs(
6377
"addConstraint(Sketcher.Constraint('PerpendicularViaPoint',%d,%d,%d,%d))",
6381
static_cast<int>(Sketcher::PointPos::start));
6385
catch (const Base::Exception& e) {
6386
Gui::NotifyUserError(Obj,
6387
QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"),
6389
Gui::Command::abortCommand();
6392
tryAutoRecompute(Obj);
6394
getSelection().clearSelection();
6398
openCommand(QT_TRANSLATE_NOOP("Command", "Add perpendicular constraint"));
6399
Gui::cmdAppObjectArgs(Obj,
6400
"addConstraint(Sketcher.Constraint('Perpendicular',%d,%d))",
6405
tryAutoRecompute(Obj);
6414
GeoId1 = selSeq.at(1).GeoId;
6415
GeoId2 = selSeq.at(2).GeoId;
6416
GeoId3 = selSeq.at(0).GeoId;
6417
PosId3 = selSeq.at(0).PosId;
6427
GeoId1 = selSeq.at(0).GeoId;
6428
GeoId2 = selSeq.at(2).GeoId;
6429
GeoId3 = selSeq.at(1).GeoId;
6430
PosId3 = selSeq.at(1).PosId;
6438
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
6441
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
6442
showNoConstraintBetweenFixedGeometry(Obj);
6446
if (isBsplinePole(Obj, GeoId1) || isBsplinePole(Obj, GeoId2)) {
6447
Gui::TranslatedUserWarning(
6449
QObject::tr("Wrong selection"),
6450
QObject::tr("Select an edge that is not a B-spline weight."));
6454
openCommand(QT_TRANSLATE_NOOP("Command", "Add perpendicular constraint"));
6456
bool safe = addConstraintSafely(Obj, [&]() {
6458
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {
6459
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
6460
if (!(geom1 && isBSplineCurve(*geom1))) {
6461
Gui::cmdAppObjectArgs(
6463
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
6465
static_cast<int>(PosId3),
6470
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
6471
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
6472
if (!(geom2 && isBSplineCurve(*geom2))) {
6473
Gui::cmdAppObjectArgs(
6475
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
6477
static_cast<int>(PosId3),
6482
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {
6484
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
6485
if (!(geom1 && isBSplineCurve(*geom1))) {
6486
Gui::cmdAppObjectArgs(
6488
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
6490
static_cast<int>(PosId3),
6495
Gui::cmdAppObjectArgs(
6497
"addConstraint(Sketcher.Constraint('PerpendicularViaPoint',%d,%d,%d,%d))",
6501
static_cast<int>(PosId3));
6503
removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3);
6511
tryAutoRecompute(Obj);
6514
getSelection().clearSelection();
6522
class CmdSketcherConstrainTangent: public CmdSketcherConstraint
6525
CmdSketcherConstrainTangent();
6526
~CmdSketcherConstrainTangent() override
6528
const char* className() const override
6530
return "CmdSketcherConstrainTangent";
6534
void activated(int iMsg) override;
6535
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
6537
static bool substituteConstraintCombinations(SketchObject* Obj, int GeoId1, int GeoId2);
6540
CmdSketcherConstrainTangent::CmdSketcherConstrainTangent()
6541
: CmdSketcherConstraint("Sketcher_ConstrainTangent")
6543
sAppModule = "Sketcher";
6544
sGroup = "Sketcher";
6545
sMenuText = QT_TR_NOOP("Constrain tangent or collinear");
6546
sToolTipText = QT_TR_NOOP("Create a tangent or collinear constraint between two entities");
6547
sWhatsThis = "Sketcher_ConstrainTangent";
6548
sStatusTip = sToolTipText;
6549
sPixmap = "Constraint_Tangent";
6553
allowedSelSequences = {
6554
{SelEdge, SelEdgeOrAxis},
6555
{SelEdgeOrAxis, SelEdge},
6556
{SelEdge, SelExternalEdge},
6557
{SelExternalEdge, SelEdge},
6558
{SelVertexOrRoot, SelEdge, SelEdgeOrAxis},
6559
{SelVertexOrRoot, SelEdgeOrAxis, SelEdge},
6560
{SelVertexOrRoot, SelEdge, SelExternalEdge},
6561
{SelVertexOrRoot, SelExternalEdge, SelEdge},
6562
{SelEdge, SelVertexOrRoot, SelEdgeOrAxis},
6563
{SelEdgeOrAxis, SelVertexOrRoot, SelEdge},
6564
{SelEdge, SelVertexOrRoot, SelExternalEdge},
6565
{SelExternalEdge, SelVertexOrRoot, SelEdge},
6566
{SelVertexOrRoot, SelVertex} };
6569
bool CmdSketcherConstrainTangent::substituteConstraintCombinations(SketchObject* Obj,
6573
const std::vector<Constraint*>& cvals = Obj->Constraints.getValues();
6576
for (std::vector<Constraint*>::const_iterator it = cvals.begin(); it != cvals.end();
6578
if ((*it)->Type == Sketcher::Coincident
6579
&& (((*it)->First == GeoId1 && (*it)->Second == GeoId2)
6580
|| ((*it)->Second == GeoId1 && (*it)->First == GeoId2))
6581
&& ((*it)->FirstPos == Sketcher::PointPos::start
6582
|| (*it)->FirstPos == Sketcher::PointPos::end)
6583
&& ((*it)->SecondPos == Sketcher::PointPos::start
6584
|| (*it)->SecondPos == Sketcher::PointPos::end)) {
6587
int first = (*it)->First;
6588
int firstpos = static_cast<int>((*it)->FirstPos);
6590
Gui::Command::openCommand(
6591
QT_TRANSLATE_NOOP("Command", "Swap coincident+tangency with ptp tangency"));
6593
doEndpointTangency(Obj, (*it)->First, (*it)->Second, (*it)->FirstPos, (*it)->SecondPos);
6595
Gui::cmdAppObjectArgs(Obj, "delConstraintOnPoint(%d,%d)", first, firstpos);
6600
tryAutoRecomputeIfNotSolve(Obj);
6602
notifyConstraintSubstitutions(QObject::tr("Endpoint to endpoint tangency was applied. "
6603
"The coincident constraint was deleted."));
6605
getSelection().clearSelection();
6608
else if ((*it)->Type == Sketcher::PointOnObject
6609
&& (((*it)->First == GeoId1 && (*it)->Second == GeoId2)
6610
|| ((*it)->Second == GeoId1 && (*it)->First == GeoId2))
6611
&& ((*it)->FirstPos == Sketcher::PointPos::start
6612
|| (*it)->FirstPos == Sketcher::PointPos::end)) {
6613
Gui::Command::openCommand(
6614
QT_TRANSLATE_NOOP("Command",
6615
"Swap point on object and tangency with point to curve tangency"));
6617
doEndpointToEdgeTangency(Obj, (*it)->First, (*it)->FirstPos, (*it)->Second);
6619
Gui::cmdAppObjectArgs(Obj,
6620
"delConstraint(%d)",
6628
tryAutoRecomputeIfNotSolve(Obj);
6630
notifyConstraintSubstitutions(QObject::tr("Endpoint to edge tangency was applied. The "
6631
"point on object constraint was deleted."));
6633
getSelection().clearSelection();
6641
void CmdSketcherConstrainTangent::activated(int iMsg)
6646
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
6649
if (selection.size() != 1
6650
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
6651
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
6652
"User parameter:BaseApp/Preferences/Mod/Sketcher");
6653
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
6655
if (constraintMode) {
6656
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
6657
getSelection().clearSelection();
6660
QString strBasicHelp =
6661
QObject::tr("There are a number of ways this constraint can be applied.\n\n"
6662
"Accepted combinations: two curves; an endpoint and a curve; two "
6663
"endpoints; two curves and a point.",
6664
"tangent constraint");
6666
QObject::tr("Select some geometry from the sketch.", "tangent constraint");
6667
strError.append(QString::fromLatin1("\n\n"));
6668
strError.append(strBasicHelp);
6669
Gui::TranslatedUserWarning(getActiveGuiDocument(),
6670
QObject::tr("Wrong selection"),
6671
std::move(strError));
6677
const std::vector<std::string>& SubNames = selection[0].getSubNames();
6678
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
6680
if (SubNames.size() != 2 && SubNames.size() != 3) {
6681
Gui::TranslatedUserWarning(Obj,
6682
QObject::tr("Wrong selection"),
6683
QObject::tr("Wrong number of selected objects!"));
6687
int GeoId1, GeoId2, GeoId3;
6688
Sketcher::PointPos PosId1, PosId2, PosId3;
6689
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
6690
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
6692
if (areBothPointsOrSegmentsFixed(Obj,
6695
showNoConstraintBetweenFixedGeometry(Obj);
6698
if (SubNames.size() == 3) {
6699
getIdsFromName(SubNames[2], Obj, GeoId3, PosId3);
6702
if (isVertex(GeoId1, PosId1)) {
6703
std::swap(GeoId1, GeoId2);
6704
std::swap(PosId1, PosId2);
6706
if (isVertex(GeoId2, PosId2)) {
6707
std::swap(GeoId2, GeoId3);
6708
std::swap(PosId2, PosId3);
6711
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
6713
if (isBsplinePole(Obj, GeoId1) || isBsplinePole(Obj, GeoId2)) {
6714
Gui::TranslatedUserWarning(
6716
QObject::tr("Wrong selection"),
6717
QObject::tr("Select an edge that is not a B-spline weight."));
6721
openCommand(QT_TRANSLATE_NOOP("Command", "Add tangent constraint"));
6723
bool safe = addConstraintSafely(Obj, [&]() {
6725
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {
6726
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
6727
if (!(geom1 && isBSplineCurve(*geom1))) {
6728
Gui::cmdAppObjectArgs(
6729
selection[0].getObject(),
6730
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
6732
static_cast<int>(PosId3),
6737
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
6738
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
6739
if (!(geom2 && isBSplineCurve(*geom2))) {
6740
Gui::cmdAppObjectArgs(
6741
selection[0].getObject(),
6742
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
6744
static_cast<int>(PosId3),
6749
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {
6751
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
6752
if (!(geom1 && isBSplineCurve(*geom1))) {
6753
Gui::cmdAppObjectArgs(
6754
selection[0].getObject(),
6755
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
6757
static_cast<int>(PosId3),
6762
Gui::cmdAppObjectArgs(
6763
selection[0].getObject(),
6764
"addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d))",
6768
static_cast<int>(PosId3));
6770
removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3);
6778
tryAutoRecompute(Obj);
6781
getSelection().clearSelection();
6786
Gui::TranslatedUserWarning(
6788
QObject::tr("Wrong selection"),
6789
QObject::tr("With 3 objects, there must be 2 curves and 1 point."));
6791
else if (SubNames.size() == 2) {
6793
if (isVertex(GeoId1, PosId1) && isVertex(GeoId2, PosId2)) {
6795
if (isBsplineKnot(Obj, GeoId2)) {
6796
std::swap(GeoId1, GeoId2);
6797
std::swap(PosId1, PosId2);
6800
if (isSimpleVertex(Obj, GeoId1, PosId1) || isSimpleVertex(Obj, GeoId2, PosId2)) {
6802
if (isBsplineKnot(Obj, GeoId1)) {
6803
const Part::Geometry* geom2 = Obj->getGeometry(GeoId2);
6805
if (! geom2 || ! isLineSegment(*geom2)) {
6806
Gui::TranslatedUserWarning(
6808
QObject::tr("Wrong selection"),
6809
QObject::tr("Tangent constraint at B-spline knot is only supported "
6815
Gui::TranslatedUserWarning(
6817
QObject::tr("Wrong selection"),
6818
QObject::tr("Cannot add a tangency constraint at an unconnected point!"));
6823
openCommand(QT_TRANSLATE_NOOP("Command", "Add tangent constraint"));
6824
doEndpointTangency(Obj, GeoId1, GeoId2, PosId1, PosId2);
6826
tryAutoRecompute(Obj);
6828
getSelection().clearSelection();
6831
else if ((isVertex(GeoId1, PosId1) && isEdge(GeoId2, PosId2))
6832
|| (isEdge(GeoId1, PosId1)
6833
&& isVertex(GeoId2, PosId2))) {
6834
if (isVertex(GeoId2, PosId2)) {
6835
std::swap(GeoId1, GeoId2);
6836
std::swap(PosId1, PosId2);
6839
if (isSimpleVertex(Obj, GeoId1, PosId1)) {
6840
if (isBsplineKnot(Obj, GeoId1)) {
6841
const Part::Geometry* geom2 = Obj->getGeometry(GeoId2);
6843
if (!geom2 || ! isLineSegment(*geom2)) {
6844
Gui::TranslatedUserWarning(
6846
QObject::tr("Wrong selection"),
6847
QObject::tr("Tangent constraint at B-spline knot is only supported "
6853
Gui::TranslatedUserWarning(
6855
QObject::tr("Wrong selection"),
6856
QObject::tr("Cannot add a tangency constraint at an unconnected point!"));
6861
const Part::Geometry* geom2 = Obj->getGeometry(GeoId2);
6863
if (isBsplinePole(geom2)) {
6864
Gui::TranslatedUserWarning(
6866
QObject::tr("Wrong selection"),
6867
QObject::tr("Select an edge that is not a B-spline weight."));
6871
if (!substituteConstraintCombinations(Obj, GeoId1, GeoId2)) {
6872
openCommand(QT_TRANSLATE_NOOP("Command", "Add tangent constraint"));
6873
Gui::cmdAppObjectArgs(selection[0].getObject(),
6874
"addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d))",
6876
static_cast<int>(PosId1),
6879
tryAutoRecompute(Obj);
6881
getSelection().clearSelection();
6885
else if (isEdge(GeoId1, PosId1)
6886
&& isEdge(GeoId2, PosId2)) {
6888
const Part::Geometry* geom1 = Obj->getGeometry(GeoId1);
6889
const Part::Geometry* geom2 = Obj->getGeometry(GeoId2);
6891
if (isBsplinePole(geom1) || isBsplinePole(geom2)) {
6892
Gui::TranslatedUserWarning(
6894
QObject::tr("Wrong selection"),
6895
QObject::tr("Select an edge that is not a B-spline weight."));
6903
if (substituteConstraintCombinations(Obj, GeoId1, GeoId2)) {
6907
if (geom1 && geom2 && (isEllipse(*geom1) || isEllipse(*geom2))) {
6908
if (! isEllipse(*geom1)) {
6909
std::swap(GeoId1, GeoId2);
6913
geom1 = Obj->getGeometry(GeoId1);
6914
geom2 = Obj->getGeometry(GeoId2);
6916
if (isEllipse(*geom2) || isArcOfEllipse(*geom2) || isCircle(*geom2) || isArcOfCircle(*geom2)) {
6917
Gui::Command::openCommand(
6918
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
6919
makeTangentToEllipseviaNewPoint(Obj,
6920
static_cast<const Part::GeomEllipse*>(geom1),
6924
getSelection().clearSelection();
6927
else if (isArcOfHyperbola(*geom2)) {
6928
Gui::Command::openCommand(
6929
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
6930
makeTangentToArcOfHyperbolaviaNewPoint(
6932
static_cast<const Part::GeomArcOfHyperbola*>(geom2),
6936
getSelection().clearSelection();
6939
else if (isArcOfParabola(*geom2)) {
6940
Gui::Command::openCommand(
6941
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
6942
makeTangentToArcOfParabolaviaNewPoint(
6944
static_cast<const Part::GeomArcOfParabola*>(geom2),
6948
getSelection().clearSelection();
6952
else if (geom1 && geom2 && (isArcOfEllipse(*geom1) || isArcOfEllipse(*geom2))) {
6953
if (! isArcOfEllipse(*geom1)) {
6954
std::swap(GeoId1, GeoId2);
6958
geom1 = Obj->getGeometry(GeoId1);
6959
geom2 = Obj->getGeometry(GeoId2);
6961
if (isArcOfHyperbola(*geom2) || isArcOfEllipse(*geom2)
6962
|| isCircle(*geom2) || isArcOfCircle(*geom2) || isLineSegment(*geom2)) {
6964
Gui::Command::openCommand(
6965
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
6966
makeTangentToArcOfEllipseviaNewPoint(
6968
static_cast<const Part::GeomArcOfEllipse*>(geom1),
6973
getSelection().clearSelection();
6976
else if (isArcOfParabola(*geom2)) {
6977
Gui::Command::openCommand(
6978
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
6979
makeTangentToArcOfParabolaviaNewPoint(
6981
static_cast<const Part::GeomArcOfParabola*>(geom2),
6985
getSelection().clearSelection();
6989
else if (geom1 && geom2 && (isArcOfHyperbola(*geom1) || isArcOfHyperbola(*geom2))) {
6990
if (! isArcOfHyperbola(*geom1)) {
6991
std::swap(GeoId1, GeoId2);
6995
geom1 = Obj->getGeometry(GeoId1);
6996
geom2 = Obj->getGeometry(GeoId2);
6998
if (isArcOfHyperbola(*geom2) || isArcOfEllipse(*geom2) || isCircle(*geom2)
6999
|| isArcOfCircle(*geom2) || isLineSegment(*geom2)) {
7001
Gui::Command::openCommand(
7002
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
7003
makeTangentToArcOfHyperbolaviaNewPoint(
7005
static_cast<const Part::GeomArcOfHyperbola*>(geom1),
7009
getSelection().clearSelection();
7012
else if (isArcOfParabola(*geom2)) {
7013
Gui::Command::openCommand(
7014
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
7015
makeTangentToArcOfParabolaviaNewPoint(
7017
static_cast<const Part::GeomArcOfParabola*>(geom2),
7021
getSelection().clearSelection();
7025
else if (geom1 && geom2 && (isArcOfParabola(*geom1) || isArcOfParabola(*geom2))) {
7026
if (! isArcOfParabola(*geom1)) {
7027
std::swap(GeoId1, GeoId2);
7031
geom1 = Obj->getGeometry(GeoId1);
7032
geom2 = Obj->getGeometry(GeoId2);
7034
if (isArcOfParabola(*geom2) || isArcOfHyperbola(*geom2)
7035
|| isArcOfEllipse(*geom2) || isCircle(*geom2)
7036
|| isArcOfCircle(*geom2) || isLineSegment(*geom2)) {
7038
Gui::Command::openCommand(
7039
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
7040
makeTangentToArcOfParabolaviaNewPoint(
7042
static_cast<const Part::GeomArcOfParabola*>(geom1),
7046
getSelection().clearSelection();
7050
else if (geom1 && geom2 && (isBSplineCurve(*geom1) || isBSplineCurve(*geom2))) {
7051
Gui::TranslatedUserWarning(
7053
QObject::tr("Wrong selection"),
7054
QObject::tr("Only tangent-via-point is supported with a B-spline."));
7055
getSelection().clearSelection();
7059
openCommand(QT_TRANSLATE_NOOP("Command", "Add tangent constraint"));
7060
Gui::cmdAppObjectArgs(selection[0].getObject(),
7061
"addConstraint(Sketcher.Constraint('Tangent',%d,%d))",
7065
tryAutoRecompute(Obj);
7067
getSelection().clearSelection();
7075
void CmdSketcherConstrainTangent::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
7077
SketcherGui::ViewProviderSketch* sketchgui =
7078
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
7079
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
7081
int GeoId1 = GeoEnum::GeoUndef, GeoId2 = GeoEnum::GeoUndef, GeoId3 = GeoEnum::GeoUndef;
7082
Sketcher::PointPos PosId1 = Sketcher::PointPos::none, PosId2 = Sketcher::PointPos::none,
7083
PosId3 = Sketcher::PointPos::none;
7091
GeoId1 = selSeq.at(0).GeoId;
7092
GeoId2 = selSeq.at(1).GeoId;
7095
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
7096
showNoConstraintBetweenFixedGeometry(Obj);
7100
const Part::Geometry* geom1 = Obj->getGeometry(GeoId1);
7101
const Part::Geometry* geom2 = Obj->getGeometry(GeoId2);
7103
if (isBsplinePole(geom1) || isBsplinePole(geom2)) {
7104
Gui::TranslatedUserWarning(
7106
QObject::tr("Wrong selection"),
7107
QObject::tr("Select an edge that is not a B-spline weight."));
7115
if (substituteConstraintCombinations(Obj, GeoId1, GeoId2)) {
7119
if (geom1 && geom2 && (isEllipse(*geom1) || isEllipse(*geom2))) {
7120
if (! isEllipse(*geom1)) {
7121
std::swap(GeoId1, GeoId2);
7125
geom1 = Obj->getGeometry(GeoId1);
7126
geom2 = Obj->getGeometry(GeoId2);
7128
if (isEllipse(*geom2) || isArcOfEllipse(*geom2)
7129
|| isCircle(*geom2) || isArcOfCircle(*geom2)) {
7131
Gui::Command::openCommand(
7132
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
7133
makeTangentToEllipseviaNewPoint(Obj,
7134
static_cast<const Part::GeomEllipse*>(geom1),
7138
getSelection().clearSelection();
7141
else if (isArcOfHyperbola(*geom2)) {
7142
Gui::Command::openCommand(
7143
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
7144
makeTangentToArcOfHyperbolaviaNewPoint(
7146
static_cast<const Part::GeomArcOfHyperbola*>(geom2),
7150
getSelection().clearSelection();
7153
else if (isArcOfParabola(*geom2)) {
7154
Gui::Command::openCommand(
7155
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
7156
makeTangentToArcOfParabolaviaNewPoint(
7158
static_cast<const Part::GeomArcOfParabola*>(geom2),
7162
getSelection().clearSelection();
7166
else if (geom1 && geom2 && (isArcOfHyperbola(*geom1) || isArcOfHyperbola(*geom2))) {
7167
if (! isArcOfHyperbola(*geom1)) {
7168
std::swap(GeoId1, GeoId2);
7172
geom1 = Obj->getGeometry(GeoId1);
7173
geom2 = Obj->getGeometry(GeoId2);
7175
if (isArcOfHyperbola(*geom2) || isArcOfEllipse(*geom2) || isCircle(*geom2)
7176
|| isArcOfCircle(*geom2) || isLineSegment(*geom2)) {
7178
Gui::Command::openCommand(
7179
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
7180
makeTangentToArcOfHyperbolaviaNewPoint(
7182
static_cast<const Part::GeomArcOfHyperbola*>(geom1),
7186
getSelection().clearSelection();
7189
else if (isArcOfParabola(*geom2)) {
7190
Gui::Command::openCommand(
7191
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
7192
makeTangentToArcOfParabolaviaNewPoint(
7194
static_cast<const Part::GeomArcOfParabola*>(geom2),
7198
getSelection().clearSelection();
7202
else if (geom1 && geom2 && (isArcOfParabola(*geom1) || isArcOfParabola(*geom2))) {
7203
if (! isArcOfParabola(*geom1)) {
7204
std::swap(GeoId1, GeoId2);
7208
geom1 = Obj->getGeometry(GeoId1);
7209
geom2 = Obj->getGeometry(GeoId2);
7211
if (isArcOfParabola(*geom2) || isArcOfHyperbola(*geom2) || isArcOfEllipse(*geom2)
7212
|| isCircle(*geom2) || isArcOfCircle(*geom2) || isLineSegment(*geom2)) {
7214
Gui::Command::openCommand(
7215
QT_TRANSLATE_NOOP("Command", "Add tangent constraint point"));
7216
makeTangentToArcOfParabolaviaNewPoint(
7218
static_cast<const Part::GeomArcOfParabola*>(geom1),
7222
getSelection().clearSelection();
7227
openCommand(QT_TRANSLATE_NOOP("Command", "Add tangent constraint"));
7228
Gui::cmdAppObjectArgs(Obj,
7229
"addConstraint(Sketcher.Constraint('Tangent',%d,%d))",
7233
tryAutoRecompute(Obj);
7243
GeoId1 = selSeq.at(1).GeoId;
7244
GeoId2 = selSeq.at(2).GeoId;
7245
GeoId3 = selSeq.at(0).GeoId;
7246
PosId3 = selSeq.at(0).PosId;
7256
GeoId1 = selSeq.at(0).GeoId;
7257
GeoId2 = selSeq.at(2).GeoId;
7258
GeoId3 = selSeq.at(1).GeoId;
7259
PosId3 = selSeq.at(1).PosId;
7266
GeoId1 = selSeq.at(0).GeoId;
7267
GeoId2 = selSeq.at(1).GeoId;
7268
PosId1 = selSeq.at(0).PosId;
7269
PosId2 = selSeq.at(1).PosId;
7272
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
7273
showNoConstraintBetweenFixedGeometry(Obj);
7277
if (isSimpleVertex(Obj, GeoId1, PosId1) || isSimpleVertex(Obj, GeoId2, PosId2)) {
7278
Gui::TranslatedUserWarning(
7280
QObject::tr("Wrong selection"),
7281
QObject::tr("Cannot add a tangency constraint at an unconnected point!"));
7286
const Part::Geometry* geom1 = Obj->getGeometry(GeoId1);
7287
const Part::Geometry* geom2 = Obj->getGeometry(GeoId2);
7289
if (geom1 && geom2 && (isBSplineCurve(*geom1) || isBSplineCurve(*geom2))) {
7290
if (! isBSplineCurve(*geom1)) {
7291
std::swap(GeoId1, GeoId2);
7292
std::swap(PosId1, PosId2);
7297
openCommand(QT_TRANSLATE_NOOP("Command", "Add tangent constraint"));
7298
Gui::cmdAppObjectArgs(Obj,
7299
"addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d,%d))",
7301
static_cast<int>(PosId1),
7303
static_cast<int>(PosId2));
7305
tryAutoRecompute(Obj);
7307
getSelection().clearSelection();
7314
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
7317
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
7318
showNoConstraintBetweenFixedGeometry(Obj);
7322
if (isBsplinePole(Obj, GeoId1) || isBsplinePole(Obj, GeoId2)) {
7323
Gui::TranslatedUserWarning(
7325
QObject::tr("Wrong selection"),
7326
QObject::tr("Select an edge that is not a B-spline weight."));
7330
openCommand(QT_TRANSLATE_NOOP("Command", "Add tangent constraint"));
7332
bool safe = addConstraintSafely(Obj, [&]() {
7334
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {
7335
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
7336
if (!(geom1 && isBSplineCurve(*geom1))) {
7337
Gui::cmdAppObjectArgs(
7339
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
7341
static_cast<int>(PosId3),
7346
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
7347
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
7348
if (!(geom2 && isBSplineCurve(*geom2))) {
7349
Gui::cmdAppObjectArgs(
7351
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
7353
static_cast<int>(PosId3),
7358
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {
7360
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
7361
if (!(geom1 && isBSplineCurve(*geom1))) {
7362
Gui::cmdAppObjectArgs(
7364
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
7366
static_cast<int>(PosId3),
7371
Gui::cmdAppObjectArgs(
7373
"addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d))",
7377
static_cast<int>(PosId3));
7379
removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3);
7387
tryAutoRecompute(Obj);
7390
getSelection().clearSelection();
7398
class CmdSketcherConstrainRadius: public CmdSketcherConstraint
7401
CmdSketcherConstrainRadius();
7402
~CmdSketcherConstrainRadius() override
7404
void updateAction(int mode) override;
7405
const char* className() const override
7407
return "CmdSketcherConstrainRadius";
7411
void activated(int iMsg) override;
7412
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
7415
CmdSketcherConstrainRadius::CmdSketcherConstrainRadius()
7416
: CmdSketcherConstraint("Sketcher_ConstrainRadius")
7418
sAppModule = "Sketcher";
7419
sGroup = "Sketcher";
7420
sMenuText = QT_TR_NOOP("Constrain radius");
7421
sToolTipText = QT_TR_NOOP("Fix the radius of a circle or an arc");
7422
sWhatsThis = "Sketcher_ConstrainRadius";
7423
sStatusTip = sToolTipText;
7424
sPixmap = "Constraint_Radius";
7428
allowedSelSequences = {{SelEdge}, {SelExternalEdge}};
7431
void CmdSketcherConstrainRadius::activated(int iMsg)
7435
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
7438
if (selection.size() != 1
7439
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
7440
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
7441
"User parameter:BaseApp/Preferences/Mod/Sketcher");
7442
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
7444
if (constraintMode) {
7445
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
7446
getSelection().clearSelection();
7450
Gui::TranslatedUserWarning(getActiveGuiDocument(),
7451
QObject::tr("Wrong selection"),
7452
QObject::tr("Select the right things from the sketch."));
7458
const std::vector<std::string>& SubNames = selection[0].getSubNames();
7459
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
7461
if (SubNames.empty()) {
7462
Gui::TranslatedUserWarning(
7464
QObject::tr("Wrong selection"),
7465
QObject::tr("Select one or more arcs or circles from the sketch."));
7470
std::vector<std::pair<int, double>> geoIdRadiusMap;
7471
std::vector<std::pair<int, double>> externalGeoIdRadiusMap;
7474
bool nonpoles = false;
7476
for (auto& subname : SubNames) {
7477
bool issegmentfixed = false;
7480
if (subname.size() > 4 && subname.substr(0, 4) == "Edge") {
7481
GeoId = std::atoi(subname.substr(4, 4000).c_str()) - 1;
7482
issegmentfixed = isPointOrSegmentFixed(Obj, GeoId);
7484
else if (subname.size() > 4 && subname.substr(0, 12) == "ExternalEdge") {
7485
GeoId = -std::atoi(subname.substr(12, 4000).c_str()) - 2;
7486
issegmentfixed = true;
7492
const Part::Geometry* geom = Obj->getGeometry(GeoId);
7494
if (geom && isArcOfCircle(*geom)) {
7495
auto arc = static_cast<const Part::GeomArcOfCircle*>(geom);
7496
double radius = arc->getRadius();
7498
if (issegmentfixed) {
7499
externalGeoIdRadiusMap.emplace_back(GeoId, radius);
7502
geoIdRadiusMap.emplace_back(GeoId, radius);
7507
else if (geom && isCircle(*geom)) {
7508
auto circle = static_cast<const Part::GeomCircle*>(geom);
7509
double radius = circle->getRadius();
7511
if (issegmentfixed) {
7512
externalGeoIdRadiusMap.emplace_back(GeoId, radius);
7515
geoIdRadiusMap.emplace_back(GeoId, radius);
7518
if (isBsplinePole(geom)) {
7527
if (geoIdRadiusMap.empty() && externalGeoIdRadiusMap.empty()) {
7528
Gui::TranslatedUserWarning(
7530
QObject::tr("Wrong selection"),
7531
QObject::tr("Select one or more arcs or circles from the sketch."));
7535
if (poles && nonpoles) {
7536
Gui::TranslatedUserWarning(
7538
QObject::tr("Wrong selection"),
7539
QObject::tr("Select either only one or more B-spline poles or only one or more arcs or "
7540
"circles from the sketch, but not mixed."));
7544
bool commitNeeded = false;
7545
bool updateNeeded = false;
7546
bool commandopened = false;
7548
if (!externalGeoIdRadiusMap.empty()) {
7550
openCommand(QT_TRANSLATE_NOOP("Command", "Add radius constraint"));
7551
commandopened = true;
7552
unsigned int constrSize = 0;
7554
for (std::vector<std::pair<int, double>>::iterator it = externalGeoIdRadiusMap.begin();
7555
it != externalGeoIdRadiusMap.end();
7559
Gui::cmdAppObjectArgs(selection[0].getObject(),
7560
"addConstraint(Sketcher.Constraint('Radius',%d,%f))",
7565
Gui::cmdAppObjectArgs(selection[0].getObject(),
7566
"addConstraint(Sketcher.Constraint('Weight',%d,%f))",
7571
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
7573
constrSize = ConStr.size();
7575
Gui::cmdAppObjectArgs(selection[0].getObject(),
7576
"setDriving(%d,%s)",
7581
finishDatumConstraint(this, Obj, false, externalGeoIdRadiusMap.size());
7583
commitNeeded = true;
7584
updateNeeded = true;
7587
if (!geoIdRadiusMap.empty()) {
7588
if (geoIdRadiusMap.size() > 1 && constraintCreationMode == Driving) {
7590
int refGeoId = geoIdRadiusMap.front().first;
7591
double radius = geoIdRadiusMap.front().second;
7593
if (!commandopened) {
7594
openCommand(QT_TRANSLATE_NOOP("Command", "Add radius constraint"));
7598
for (std::vector<std::pair<int, double>>::iterator it = geoIdRadiusMap.begin() + 1;
7599
it != geoIdRadiusMap.end();
7601
Gui::cmdAppObjectArgs(selection[0].getObject(),
7602
"addConstraint(Sketcher.Constraint('Equal',%d,%d))",
7608
Gui::cmdAppObjectArgs(selection[0].getObject(),
7609
"addConstraint(Sketcher.Constraint('Radius',%d,%f))",
7614
Gui::cmdAppObjectArgs(selection[0].getObject(),
7615
"addConstraint(Sketcher.Constraint('Weight',%d,%f))",
7622
if (!commandopened) {
7623
openCommand(QT_TRANSLATE_NOOP("Command", "Add radius constraint"));
7625
for (std::vector<std::pair<int, double>>::iterator it = geoIdRadiusMap.begin();
7626
it != geoIdRadiusMap.end();
7629
Gui::cmdAppObjectArgs(selection[0].getObject(),
7630
"addConstraint(Sketcher.Constraint('Radius',%d,%f))",
7635
Gui::cmdAppObjectArgs(selection[0].getObject(),
7636
"addConstraint(Sketcher.Constraint('Weight',%d,%f))",
7641
if (constraintCreationMode == Reference) {
7642
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
7643
Gui::cmdAppObjectArgs(selection[0].getObject(),
7644
"setDriving(%d,%s)",
7651
finishDatumConstraint(this, Obj, constraintCreationMode == Driving);
7654
getSelection().clearSelection();
7662
tryAutoRecomputeIfNotSolve(Obj);
7666
void CmdSketcherConstrainRadius::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
7668
SketcherGui::ViewProviderSketch* sketchgui =
7669
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
7670
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
7672
int GeoId = selSeq.at(0).GeoId;
7673
double radius = 0.0;
7675
bool updateNeeded = false;
7681
const Part::Geometry* geom = Obj->getGeometry(GeoId);
7683
if (geom && isArcOfCircle(*geom)) {
7684
auto arc = static_cast<const Part::GeomArcOfCircle*>(geom);
7685
radius = arc->getRadius();
7687
else if (geom && isCircle(*geom)) {
7688
auto circle = static_cast<const Part::GeomCircle*>(geom);
7689
radius = circle->getRadius();
7692
Gui::TranslatedUserWarning(
7694
QObject::tr("Wrong selection"),
7695
QObject::tr("Constraint only applies to arcs or circles."));
7700
openCommand(QT_TRANSLATE_NOOP("Command", "Add radius constraint"));
7702
bool ispole = isBsplinePole(geom);
7705
Gui::cmdAppObjectArgs(Obj,
7706
"addConstraint(Sketcher.Constraint('Weight',%d,%f))",
7711
Gui::cmdAppObjectArgs(Obj,
7712
"addConstraint(Sketcher.Constraint('Radius',%d,%f))",
7717
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
7719
bool fixed = isPointOrSegmentFixed(Obj, GeoId);
7720
if (fixed || constraintCreationMode == Reference) {
7721
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
7723
updateNeeded = true;
7727
finishDatumConstraint(this, Obj, constraintCreationMode == Driving && !fixed);
7730
getSelection().clearSelection();
7735
tryAutoRecomputeIfNotSolve(
7742
void CmdSketcherConstrainRadius::updateAction(int mode)
7747
getAction()->setIcon(
7748
Gui::BitmapFactory().iconFromTheme("Constraint_Radius_Driven"));
7753
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radius"));
7761
class CmdSketcherConstrainDiameter: public CmdSketcherConstraint
7764
CmdSketcherConstrainDiameter();
7765
~CmdSketcherConstrainDiameter() override
7767
void updateAction(int mode) override;
7768
const char* className() const override
7770
return "CmdSketcherConstrainDiameter";
7774
void activated(int iMsg) override;
7775
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
7778
CmdSketcherConstrainDiameter::CmdSketcherConstrainDiameter()
7779
: CmdSketcherConstraint("Sketcher_ConstrainDiameter")
7781
sAppModule = "Sketcher";
7782
sGroup = "Sketcher";
7783
sMenuText = QT_TR_NOOP("Constrain diameter");
7784
sToolTipText = QT_TR_NOOP("Fix the diameter of a circle or an arc");
7785
sWhatsThis = "Sketcher_ConstrainDiameter";
7786
sStatusTip = sToolTipText;
7787
sPixmap = "Constraint_Diameter";
7791
allowedSelSequences = {{SelEdge}, {SelExternalEdge}};
7794
void CmdSketcherConstrainDiameter::activated(int iMsg)
7798
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
7801
if (selection.size() != 1
7802
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
7803
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
7804
"User parameter:BaseApp/Preferences/Mod/Sketcher");
7805
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
7807
if (constraintMode) {
7808
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
7809
getSelection().clearSelection();
7813
Gui::TranslatedUserWarning(getActiveGuiDocument(),
7814
QObject::tr("Wrong selection"),
7815
QObject::tr("Select the right things from the sketch."));
7821
const std::vector<std::string>& SubNames = selection[0].getSubNames();
7822
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
7824
if (SubNames.empty()) {
7825
Gui::TranslatedUserWarning(
7827
QObject::tr("Wrong selection"),
7828
QObject::tr("Select one or more arcs or circles from the sketch."));
7833
std::vector<std::pair<int, double>> geoIdDiameterMap;
7834
std::vector<std::pair<int, double>> externalGeoIdDiameterMap;
7836
for (auto& subname : SubNames) {
7837
bool issegmentfixed = false;
7840
if (subname.size() > 4 && subname.substr(0, 4) == "Edge") {
7841
GeoId = std::atoi(subname.substr(4, 4000).c_str()) - 1;
7842
issegmentfixed = isPointOrSegmentFixed(Obj, GeoId);
7844
else if (subname.size() > 4 && subname.substr(0, 12) == "ExternalEdge") {
7845
GeoId = -std::atoi(subname.substr(12, 4000).c_str()) - 2;
7846
issegmentfixed = true;
7852
const Part::Geometry* geom = Obj->getGeometry(GeoId);
7854
if (geom && isArcOfCircle(*geom)) {
7855
auto arc = static_cast<const Part::GeomArcOfCircle*>(geom);
7856
double radius = arc->getRadius();
7858
if (issegmentfixed) {
7859
externalGeoIdDiameterMap.emplace_back(GeoId, 2 * radius);
7862
geoIdDiameterMap.emplace_back(GeoId, 2 * radius);
7865
else if (geom && isCircle(*geom)) {
7866
auto circle = static_cast<const Part::GeomCircle*>(geom);
7867
double radius = circle->getRadius();
7869
if (isBsplinePole(geom)) {
7870
Gui::TranslatedUserWarning(
7872
QObject::tr("Wrong selection"),
7873
QObject::tr("Select an edge that is not a B-spline weight."));
7878
if (issegmentfixed) {
7879
externalGeoIdDiameterMap.emplace_back(GeoId, 2 * radius);
7882
geoIdDiameterMap.emplace_back(GeoId, 2 * radius);
7887
if (geoIdDiameterMap.empty() && externalGeoIdDiameterMap.empty()) {
7888
Gui::TranslatedUserWarning(
7890
QObject::tr("Wrong selection"),
7891
QObject::tr("Select one or more arcs or circles from the sketch."));
7895
bool commitNeeded = false;
7896
bool updateNeeded = false;
7897
bool commandopened = false;
7899
if (!externalGeoIdDiameterMap.empty()) {
7901
openCommand(QT_TRANSLATE_NOOP("Command", "Add diameter constraint"));
7902
commandopened = true;
7903
unsigned int constrSize = 0;
7905
for (std::vector<std::pair<int, double>>::iterator it = externalGeoIdDiameterMap.begin();
7906
it != externalGeoIdDiameterMap.end();
7908
Gui::cmdAppObjectArgs(Obj,
7909
"addConstraint(Sketcher.Constraint('Diameter',%d,%f))",
7913
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
7915
constrSize = ConStr.size();
7917
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", constrSize - 1, "False");
7920
finishDatumConstraint(this, Obj, false, externalGeoIdDiameterMap.size());
7922
commitNeeded = true;
7923
updateNeeded = true;
7926
if (!geoIdDiameterMap.empty()) {
7927
if (geoIdDiameterMap.size() > 1 && constraintCreationMode == Driving) {
7929
int refGeoId = geoIdDiameterMap.front().first;
7930
double diameter = geoIdDiameterMap.front().second;
7932
if (!commandopened) {
7933
openCommand(QT_TRANSLATE_NOOP("Command", "Add diameter constraint"));
7937
for (std::vector<std::pair<int, double>>::iterator it = geoIdDiameterMap.begin() + 1;
7938
it != geoIdDiameterMap.end();
7940
Gui::cmdAppObjectArgs(Obj,
7941
"addConstraint(Sketcher.Constraint('Equal',%d,%d))",
7946
Gui::cmdAppObjectArgs(Obj,
7947
"addConstraint(Sketcher.Constraint('Diameter',%d,%f))",
7953
if (!commandopened) {
7954
openCommand(QT_TRANSLATE_NOOP("Command", "Add diameter constraint"));
7956
for (std::vector<std::pair<int, double>>::iterator it = geoIdDiameterMap.begin();
7957
it != geoIdDiameterMap.end();
7959
Gui::cmdAppObjectArgs(Obj,
7960
"addConstraint(Sketcher.Constraint('Diameter',%d,%f))",
7964
if (constraintCreationMode == Reference) {
7966
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
7968
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
7973
finishDatumConstraint(this, Obj, constraintCreationMode == Driving);
7976
getSelection().clearSelection();
7984
tryAutoRecomputeIfNotSolve(Obj);
7988
void CmdSketcherConstrainDiameter::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
7990
SketcherGui::ViewProviderSketch* sketchgui =
7991
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
7992
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
7994
int GeoId = selSeq.at(0).GeoId;
7995
double diameter = 0.0;
7997
bool updateNeeded = false;
8003
const Part::Geometry* geom = Obj->getGeometry(GeoId);
8005
if (geom && isArcOfCircle(*geom)) {
8006
auto arc = static_cast<const Part::GeomArcOfCircle*>(geom);
8007
diameter = 2 * arc->getRadius();
8009
else if (geom && isCircle(*geom)) {
8010
auto circle = static_cast<const Part::GeomCircle*>(geom);
8011
diameter = 2 * circle->getRadius();
8014
Gui::TranslatedUserWarning(
8016
QObject::tr("Wrong selection"),
8017
QObject::tr("Constraint only applies to arcs or circles."));
8021
if (isBsplinePole(geom)) {
8022
Gui::TranslatedUserWarning(
8024
QObject::tr("Wrong selection"),
8025
QObject::tr("Select an edge that is not a B-spline weight."));
8030
openCommand(QT_TRANSLATE_NOOP("Command", "Add diameter constraint"));
8031
Gui::cmdAppObjectArgs(Obj,
8032
"addConstraint(Sketcher.Constraint('Diameter',%d,%f))",
8036
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
8038
bool fixed = isPointOrSegmentFixed(Obj, GeoId);
8039
if (fixed || constraintCreationMode == Reference) {
8040
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
8041
updateNeeded = true;
8045
finishDatumConstraint(this, Obj, constraintCreationMode == Driving && !fixed);
8048
getSelection().clearSelection();
8053
tryAutoRecomputeIfNotSolve(
8060
void CmdSketcherConstrainDiameter::updateAction(int mode)
8065
getAction()->setIcon(
8066
Gui::BitmapFactory().iconFromTheme("Constraint_Diameter_Driven"));
8071
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Diameter"));
8079
class CmdSketcherConstrainRadiam: public CmdSketcherConstraint
8082
CmdSketcherConstrainRadiam();
8083
~CmdSketcherConstrainRadiam() override
8085
void updateAction(int mode) override;
8086
const char* className() const override
8088
return "CmdSketcherConstrainRadiam";
8092
void activated(int iMsg) override;
8093
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
8096
CmdSketcherConstrainRadiam::CmdSketcherConstrainRadiam()
8097
: CmdSketcherConstraint("Sketcher_ConstrainRadiam")
8099
sAppModule = "Sketcher";
8100
sGroup = "Sketcher";
8101
sMenuText = QT_TR_NOOP("Constrain auto radius/diameter");
8102
sToolTipText = QT_TR_NOOP(
8103
"Fix the diameter if a circle is chosen, or the radius if an arc/spline pole is chosen");
8104
sWhatsThis = "Sketcher_ConstrainRadiam";
8105
sStatusTip = sToolTipText;
8106
sPixmap = "Constraint_Radiam";
8110
allowedSelSequences = {{SelEdge}, {SelExternalEdge}};
8113
void CmdSketcherConstrainRadiam::activated(int iMsg)
8117
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
8120
if (selection.size() != 1
8121
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
8122
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
8123
"User parameter:BaseApp/Preferences/Mod/Sketcher");
8124
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
8126
if (constraintMode) {
8127
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
8128
getSelection().clearSelection();
8132
Gui::TranslatedUserWarning(getActiveGuiDocument(),
8133
QObject::tr("Wrong selection"),
8134
QObject::tr("Select the right things from the sketch."));
8140
const std::vector<std::string>& SubNames = selection[0].getSubNames();
8141
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
8143
if (SubNames.empty()) {
8144
Gui::TranslatedUserWarning(
8146
QObject::tr("Wrong selection"),
8147
QObject::tr("Select one or more arcs or circles from the sketch."));
8152
std::vector<std::pair<int, double>> geoIdRadiamMap;
8153
std::vector<std::pair<int, double>> externalGeoIdRadiamMap;
8156
bool nonpoles = false;
8158
for (auto& subname : SubNames) {
8159
bool issegmentfixed = false;
8162
if (subname.size() > 4 && subname.substr(0, 4) == "Edge") {
8163
GeoId = std::atoi(subname.substr(4, 4000).c_str()) - 1;
8164
issegmentfixed = isPointOrSegmentFixed(Obj, GeoId);
8166
else if (subname.size() > 4 && subname.substr(0, 12) == "ExternalEdge") {
8167
GeoId = -std::atoi(subname.substr(12, 4000).c_str()) - 2;
8168
issegmentfixed = true;
8174
const Part::Geometry* geom = Obj->getGeometry(GeoId);
8177
if (geom && isArcOfCircle(*geom)) {
8178
auto arcir = static_cast<const Part::GeomArcOfCircle*>(geom);
8179
radius = arcir->getRadius();
8182
else if (geom && isCircle(*geom)) {
8183
auto arcir = static_cast<const Part::GeomCircle*>(geom);
8184
radius = arcir->getRadius();
8185
if (isBsplinePole(geom)) {
8196
if (issegmentfixed) {
8197
externalGeoIdRadiamMap.emplace_back(GeoId, radius);
8200
geoIdRadiamMap.emplace_back(GeoId, radius);
8204
if (geoIdRadiamMap.empty() && externalGeoIdRadiamMap.empty()) {
8205
Gui::TranslatedUserWarning(
8207
QObject::tr("Wrong selection"),
8208
QObject::tr("Select one or more arcs or circles from the sketch."));
8212
if (poles && nonpoles) {
8213
Gui::TranslatedUserWarning(
8215
QObject::tr("Wrong selection"),
8216
QObject::tr("Select either only one or more B-spline poles or only one or more arcs or "
8217
"circles from the sketch, but not mixed."));
8221
bool commitNeeded = false;
8222
bool updateNeeded = false;
8223
bool commandopened = false;
8225
if (!externalGeoIdRadiamMap.empty()) {
8227
openCommand(QT_TRANSLATE_NOOP("Command", "Add radiam constraint"));
8228
commandopened = true;
8229
unsigned int constrSize = 0;
8231
for (std::vector<std::pair<int, double>>::iterator it = externalGeoIdRadiamMap.begin();
8232
it != externalGeoIdRadiamMap.end();
8234
if (isArcOfCircle(*(Obj->getGeometry(it->first)))) {
8236
Gui::cmdAppObjectArgs(Obj,
8237
"addConstraint(Sketcher.Constraint('Radius',%d,%f))",
8242
Gui::cmdAppObjectArgs(Obj,
8243
"addConstraint(Sketcher.Constraint('Weight',%d,%f))",
8249
Gui::cmdAppObjectArgs(Obj,
8250
"addConstraint(Sketcher.Constraint('Diameter',%d,%f))",
8255
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
8257
constrSize = ConStr.size();
8259
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", constrSize - 1, "False");
8262
finishDatumConstraint(this, Obj, false, externalGeoIdRadiamMap.size());
8264
commitNeeded = true;
8265
updateNeeded = true;
8268
if (!geoIdRadiamMap.empty()) {
8269
if (geoIdRadiamMap.size() > 1 && constraintCreationMode == Driving) {
8271
int refGeoId = geoIdRadiamMap.front().first;
8272
double radiam = geoIdRadiamMap.front().second;
8274
if (!commandopened) {
8275
openCommand(QT_TRANSLATE_NOOP("Command", "Add radiam constraint"));
8279
for (std::vector<std::pair<int, double>>::iterator it = geoIdRadiamMap.begin() + 1;
8280
it != geoIdRadiamMap.end();
8282
Gui::cmdAppObjectArgs(Obj,
8283
"addConstraint(Sketcher.Constraint('Equal',%d,%d))",
8289
Gui::cmdAppObjectArgs(Obj,
8290
"addConstraint(Sketcher.Constraint('Weight',%d,%f))",
8294
else if (isCircle(*(Obj->getGeometry(refGeoId)))) {
8295
Gui::cmdAppObjectArgs(Obj,
8296
"addConstraint(Sketcher.Constraint('Diameter',%d,%f))",
8301
Gui::cmdAppObjectArgs(Obj,
8302
"addConstraint(Sketcher.Constraint('Radius',%d,%f))",
8309
if (!commandopened) {
8310
openCommand(QT_TRANSLATE_NOOP("Command", "Add radiam constraint"));
8312
for (std::vector<std::pair<int, double>>::iterator it = geoIdRadiamMap.begin();
8313
it != geoIdRadiamMap.end();
8316
Gui::cmdAppObjectArgs(Obj,
8317
"addConstraint(Sketcher.Constraint('Weight',%d,%f))",
8321
else if (isCircle(*(Obj->getGeometry(it->first)))){
8322
Gui::cmdAppObjectArgs(Obj,
8323
"addConstraint(Sketcher.Constraint('Diameter',%d,%f))",
8328
Gui::cmdAppObjectArgs(Obj,
8329
"addConstraint(Sketcher.Constraint('Radius',%d,%f))",
8334
if (constraintCreationMode == Reference) {
8336
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
8338
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
8343
finishDatumConstraint(this, Obj, constraintCreationMode == Driving);
8346
getSelection().clearSelection();
8354
tryAutoRecomputeIfNotSolve(Obj);
8358
void CmdSketcherConstrainRadiam::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
8360
SketcherGui::ViewProviderSketch* sketchgui =
8361
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
8362
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
8364
int GeoId = selSeq.at(0).GeoId;
8365
double radiam = 0.0;
8367
bool updateNeeded = false;
8369
bool isCircleGeom = false;
8370
bool isPole = false;
8376
const Part::Geometry* geom = Obj->getGeometry(GeoId);
8378
if (geom && isArcOfCircle(*geom)) {
8379
auto arc = static_cast<const Part::GeomArcOfCircle*>(geom);
8380
radiam = arc->getRadius();
8382
else if (geom && isCircle(*geom)) {
8383
auto circle = static_cast<const Part::GeomCircle*>(geom);
8384
radiam = circle->getRadius();
8386
if (isBsplinePole(geom)) {
8391
Gui::TranslatedUserWarning(
8393
QObject::tr("Wrong selection"),
8394
QObject::tr("Constraint only applies to arcs or circles."));
8399
openCommand(QT_TRANSLATE_NOOP("Command", "Add radiam constraint"));
8402
Gui::cmdAppObjectArgs(Obj,
8403
"addConstraint(Sketcher.Constraint('Weight',%d,%f))",
8407
else if (isCircleGeom) {
8408
Gui::cmdAppObjectArgs(Obj,
8409
"addConstraint(Sketcher.Constraint('Diameter',%d,%f))",
8414
Gui::cmdAppObjectArgs(Obj,
8415
"addConstraint(Sketcher.Constraint('Radius',%d,%f))",
8420
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
8422
bool fixed = isPointOrSegmentFixed(Obj, GeoId);
8423
if (fixed || constraintCreationMode == Reference) {
8424
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
8425
updateNeeded = true;
8429
finishDatumConstraint(this, Obj, constraintCreationMode == Driving && !fixed);
8432
getSelection().clearSelection();
8437
tryAutoRecomputeIfNotSolve(
8444
void CmdSketcherConstrainRadiam::updateAction(int mode)
8449
getAction()->setIcon(
8450
Gui::BitmapFactory().iconFromTheme("Constraint_Radiam_Driven"));
8455
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radiam"));
8463
DEF_STD_CMD_ACLU(CmdSketcherCompConstrainRadDia)
8465
CmdSketcherCompConstrainRadDia::CmdSketcherCompConstrainRadDia()
8466
: Command("Sketcher_CompConstrainRadDia")
8468
sAppModule = "Sketcher";
8469
sGroup = "Sketcher";
8470
sMenuText = QT_TR_NOOP("Constrain arc or circle");
8471
sToolTipText = QT_TR_NOOP("Constrain an arc or a circle");
8472
sWhatsThis = "Sketcher_CompConstrainRadDia";
8473
sStatusTip = sToolTipText;
8478
void CmdSketcherCompConstrainRadDia::activated(int iMsg)
8480
Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
8482
rcCmdMgr.runCommandByName("Sketcher_ConstrainRadius");
8484
else if (iMsg == 1) {
8485
rcCmdMgr.runCommandByName("Sketcher_ConstrainDiameter");
8487
else if (iMsg == 2) {
8488
rcCmdMgr.runCommandByName("Sketcher_ConstrainRadiam");
8495
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
8496
"User parameter:BaseApp/Preferences/Mod/Sketcher");
8497
hGrp->SetInt("CurRadDiaCons", iMsg);
8501
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
8502
QList<QAction*> a = pcAction->actions();
8504
assert(iMsg < a.size());
8505
pcAction->setIcon(a[iMsg]->icon());
8508
Gui::Action* CmdSketcherCompConstrainRadDia::createAction()
8510
Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
8511
pcAction->setDropDownMenu(true);
8512
applyCommandData(this->className(), pcAction);
8514
QAction* arc1 = pcAction->addAction(QString());
8515
arc1->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radius"));
8516
QAction* arc2 = pcAction->addAction(QString());
8517
arc2->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Diameter"));
8518
QAction* arc3 = pcAction->addAction(QString());
8519
arc3->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radiam"));
8521
_pcAction = pcAction;
8524
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
8525
"User parameter:BaseApp/Preferences/Mod/Sketcher");
8526
int curRadDiaCons = hGrp->GetInt("CurRadDiaCons", 2);
8528
switch (curRadDiaCons) {
8530
pcAction->setIcon(arc1->icon());
8533
pcAction->setIcon(arc2->icon());
8536
pcAction->setIcon(arc3->icon());
8539
pcAction->setProperty("defaultAction", QVariant(curRadDiaCons));
8540
pcAction->setShortcut(QString::fromLatin1(getAccel()));
8545
void CmdSketcherCompConstrainRadDia::updateAction(int mode)
8547
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(getAction());
8552
QList<QAction*> a = pcAction->actions();
8553
int index = pcAction->property("defaultAction").toInt();
8556
a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radius_Driven"));
8557
a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Diameter_Driven"));
8558
a[2]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radiam_Driven"));
8559
getAction()->setIcon(a[index]->icon());
8562
a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radius"));
8563
a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Diameter"));
8564
a[2]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radiam"));
8565
getAction()->setIcon(a[index]->icon());
8570
void CmdSketcherCompConstrainRadDia::languageChange()
8572
Command::languageChange();
8577
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
8578
QList<QAction*> a = pcAction->actions();
8580
QAction* arc1 = a[0];
8581
arc1->setText(QApplication::translate("CmdSketcherCompConstrainRadDia", "Constrain radius"));
8582
arc1->setToolTip(QApplication::translate("Sketcher_ConstrainRadius",
8583
"Fix the radius of an arc or a circle"));
8584
arc1->setStatusTip(QApplication::translate("Sketcher_ConstrainRadius",
8585
"Fix the radius of an arc or a circle"));
8586
QAction* arc2 = a[1];
8587
arc2->setText(QApplication::translate("CmdSketcherCompConstrainRadDia", "Constrain diameter"));
8588
arc2->setToolTip(QApplication::translate("Sketcher_ConstrainDiameter",
8589
"Fix the diameter of a circle or an arc"));
8590
arc2->setStatusTip(QApplication::translate("Sketcher_ConstrainDiameter",
8591
"Fix the diameter of a circle or an arc"));
8592
QAction* arc3 = a[2];
8593
arc3->setText(QApplication::translate("CmdSketcherCompConstrainRadDia",
8594
"Constrain auto radius/diameter"));
8595
arc3->setToolTip(QApplication::translate("Sketcher_ConstrainRadiam",
8596
"Fix the radius/diameter of an arc or a circle"));
8597
arc3->setStatusTip(QApplication::translate("Sketcher_ConstrainRadiam",
8598
"Fix the radius/diameter of an arc or a circle"));
8601
bool CmdSketcherCompConstrainRadDia::isActive()
8603
return isCommandActive(getActiveGuiDocument());
8608
class CmdSketcherConstrainAngle: public CmdSketcherConstraint
8611
CmdSketcherConstrainAngle();
8612
~CmdSketcherConstrainAngle() override
8614
void updateAction(int mode) override;
8615
const char* className() const override
8617
return "CmdSketcherConstrainAngle";
8621
void activated(int iMsg) override;
8622
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
8625
CmdSketcherConstrainAngle::CmdSketcherConstrainAngle()
8626
: CmdSketcherConstraint("Sketcher_ConstrainAngle")
8628
sAppModule = "Sketcher";
8629
sGroup = "Sketcher";
8630
sMenuText = QT_TR_NOOP("Constrain angle");
8631
sToolTipText = QT_TR_NOOP("Fix the angle of a line or the angle between two lines");
8632
sWhatsThis = "Sketcher_ConstrainAngle";
8633
sStatusTip = sToolTipText;
8634
sPixmap = "Constraint_InternalAngle";
8638
allowedSelSequences = {{SelEdge, SelEdgeOrAxis},
8639
{SelEdgeOrAxis, SelEdge},
8640
{SelEdge, SelExternalEdge},
8641
{SelExternalEdge, SelEdge},
8642
{SelExternalEdge, SelExternalEdge},
8643
{SelEdge, SelVertexOrRoot, SelEdgeOrAxis},
8644
{SelEdgeOrAxis, SelVertexOrRoot, SelEdge},
8645
{SelEdge, SelVertexOrRoot, SelExternalEdge},
8646
{SelExternalEdge, SelVertexOrRoot, SelEdge},
8647
{SelExternalEdge, SelVertexOrRoot, SelExternalEdge},
8648
{SelVertexOrRoot, SelEdge, SelEdgeOrAxis},
8649
{SelVertexOrRoot, SelEdgeOrAxis, SelEdge},
8650
{SelVertexOrRoot, SelEdge, SelExternalEdge},
8651
{SelVertexOrRoot, SelExternalEdge, SelEdge},
8652
{SelVertexOrRoot, SelExternalEdge, SelExternalEdge}};
8655
void CmdSketcherConstrainAngle::activated(int iMsg)
8660
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
8663
if (selection.size() != 1
8664
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
8665
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
8666
"User parameter:BaseApp/Preferences/Mod/Sketcher");
8667
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
8669
if (constraintMode) {
8670
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
8671
getSelection().clearSelection();
8675
Gui::TranslatedUserWarning(getActiveGuiDocument(),
8676
QObject::tr("Wrong selection"),
8677
QObject::tr("Select the right things from the sketch."));
8683
const std::vector<std::string>& SubNames = selection[0].getSubNames();
8684
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
8686
if (SubNames.empty() || SubNames.size() > 3) {
8687
Gui::TranslatedUserWarning(
8689
QObject::tr("Wrong selection"),
8691
"Select one or two lines from the sketch. Or select two edges and a point."));
8695
int GeoId1, GeoId2 = GeoEnum::GeoUndef, GeoId3 = GeoEnum::GeoUndef;
8696
Sketcher::PointPos PosId1, PosId2 = Sketcher::PointPos::none, PosId3 = Sketcher::PointPos::none;
8697
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
8698
if (SubNames.size() > 1) {
8699
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
8701
if (SubNames.size() > 2) {
8702
getIdsFromName(SubNames[2], Obj, GeoId3, PosId3);
8705
if (SubNames.size() == 3) {
8709
if (isVertex(GeoId1, PosId1)) {
8710
std::swap(GeoId1, GeoId2);
8711
std::swap(PosId1, PosId2);
8713
if (isVertex(GeoId2, PosId2)) {
8714
std::swap(GeoId2, GeoId3);
8715
std::swap(PosId2, PosId3);
8718
bool bothexternal = areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2);
8720
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
8722
if (isBsplinePole(Obj, GeoId1) || isBsplinePole(Obj, GeoId2)) {
8723
Gui::TranslatedUserWarning(
8725
QObject::tr("Wrong selection"),
8726
QObject::tr("Select an edge that is not a B-spline weight."));
8730
double ActAngle = 0.0;
8732
openCommand(QT_TRANSLATE_NOOP("Command", "Add angle constraint"));
8735
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {
8736
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
8737
if (!(geom1 && isBSplineCurve(*geom1))) {
8738
Gui::cmdAppObjectArgs(
8739
selection[0].getObject(),
8740
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
8742
static_cast<int>(PosId3),
8746
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
8747
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
8748
if (!(geom2 && isBSplineCurve(*geom2))) {
8749
Gui::cmdAppObjectArgs(
8750
selection[0].getObject(),
8751
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
8753
static_cast<int>(PosId3),
8757
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {
8759
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
8760
if (!(geom1 && isBSplineCurve(*geom1))) {
8761
Gui::cmdAppObjectArgs(
8762
selection[0].getObject(),
8763
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
8765
static_cast<int>(PosId3),
8773
Base::Vector3d p = Obj->getPoint(GeoId3, PosId3);
8774
ActAngle = Obj->calculateAngleViaPoint(GeoId1, GeoId2, p.x, p.y);
8777
if (ActAngle < -Precision::Angular()) {
8778
std::swap(GeoId1, GeoId2);
8779
std::swap(PosId1, PosId2);
8780
ActAngle = -ActAngle;
8783
Gui::cmdAppObjectArgs(
8784
selection[0].getObject(),
8785
"addConstraint(Sketcher.Constraint('AngleViaPoint',%d,%d,%d,%d,%f))",
8789
static_cast<int>(PosId3),
8792
removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3);
8795
|| constraintCreationMode
8797
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
8799
Gui::cmdAppObjectArgs(selection[0].getObject(),
8800
"setDriving(%d,%s)",
8803
finishDatumConstraint(this, Obj, false);
8806
finishDatumConstraint(this, Obj, true);
8812
else if (SubNames.size() < 3) {
8814
if (isVertex(GeoId1, PosId1) && isEdge(GeoId2, PosId2)) {
8815
std::swap(GeoId1, GeoId2);
8816
std::swap(PosId1, PosId2);
8819
if (isBsplinePole(Obj, GeoId1)
8820
|| (GeoId2 != GeoEnum::GeoUndef && isBsplinePole(Obj, GeoId2))) {
8821
Gui::TranslatedUserWarning(
8823
QObject::tr("Wrong selection"),
8824
QObject::tr("Select an edge that is not a B-spline weight."));
8828
if (isEdge(GeoId2, PosId2)) {
8829
makeAngleBetweenTwoLines(Obj, this, GeoId1, GeoId2);
8832
else if (isEdge(GeoId1, PosId1)) {
8833
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
8834
Gui::TranslatedUserWarning(
8836
QObject::tr("Wrong selection"),
8837
QObject::tr("Cannot add an angle constraint on an axis!"));
8841
const Part::Geometry* geom = Obj->getGeometry(GeoId1);
8843
if (isLineSegment(*geom)) {
8844
auto lineSeg = static_cast<const Part::GeomLineSegment*>(geom);
8845
Base::Vector3d dir = lineSeg->getEndPoint() - lineSeg->getStartPoint();
8846
double ActAngle = atan2(dir.y, dir.x);
8848
openCommand(QT_TRANSLATE_NOOP("Command", "Add angle constraint"));
8849
Gui::cmdAppObjectArgs(selection[0].getObject(),
8850
"addConstraint(Sketcher.Constraint('Angle',%d,%f))",
8854
if (GeoId1 <= Sketcher::GeoEnum::RefExt || constraintCreationMode == Reference) {
8856
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
8858
Gui::cmdAppObjectArgs(selection[0].getObject(),
8859
"setDriving(%d,%s)",
8862
finishDatumConstraint(this, Obj, false);
8865
finishDatumConstraint(this, Obj, true);
8870
else if (isArcOfCircle(*geom)) {
8871
auto arc = static_cast<const Part::GeomArcOfCircle*>(geom);
8872
double angle = arc->getAngle(true);
8874
openCommand(QT_TRANSLATE_NOOP("Command", "Add angle constraint"));
8875
Gui::cmdAppObjectArgs(selection[0].getObject(),
8876
"addConstraint(Sketcher.Constraint('Angle',%d,%f))",
8880
if (GeoId1 <= Sketcher::GeoEnum::RefExt || constraintCreationMode == Reference) {
8882
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
8884
Gui::cmdAppObjectArgs(selection[0].getObject(),
8885
"setDriving(%d,%s)",
8888
finishDatumConstraint(this, Obj, false);
8891
finishDatumConstraint(this, Obj, true);
8899
Gui::TranslatedUserWarning(
8901
QObject::tr("Wrong selection"),
8902
QObject::tr("Select one or two lines from the sketch. Or select two edges and a point."));
8906
void CmdSketcherConstrainAngle::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
8908
SketcherGui::ViewProviderSketch* sketchgui =
8909
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
8910
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
8912
int GeoId1 = GeoEnum::GeoUndef, GeoId2 = GeoEnum::GeoUndef, GeoId3 = GeoEnum::GeoUndef;
8913
Sketcher::PointPos PosId1 = Sketcher::PointPos::none, PosId2 = Sketcher::PointPos::none,
8914
PosId3 = Sketcher::PointPos::none;
8923
GeoId1 = selSeq.at(0).GeoId;
8924
GeoId2 = selSeq.at(1).GeoId;
8926
makeAngleBetweenTwoLines(Obj, this, GeoId1, GeoId2);
8935
GeoId1 = selSeq.at(0).GeoId;
8936
GeoId2 = selSeq.at(2).GeoId;
8937
GeoId3 = selSeq.at(1).GeoId;
8938
PosId3 = selSeq.at(1).PosId;
8947
GeoId1 = selSeq.at(1).GeoId;
8948
GeoId2 = selSeq.at(2).GeoId;
8949
GeoId3 = selSeq.at(0).GeoId;
8950
PosId3 = selSeq.at(0).PosId;
8955
bool bothexternal = areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2);
8957
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
8959
if (isBsplinePole(Obj, GeoId1) || isBsplinePole(Obj, GeoId2)) {
8960
Gui::TranslatedUserWarning(
8962
QObject::tr("Wrong selection"),
8963
QObject::tr("Select an edge that is not a B-spline weight."));
8967
double ActAngle = 0.0;
8969
openCommand(QT_TRANSLATE_NOOP("Command", "Add angle constraint"));
8972
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {
8973
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
8974
if (!(geom1 && isBSplineCurve(*geom1))) {
8975
Gui::cmdAppObjectArgs(Obj,
8976
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
8978
static_cast<int>(PosId3),
8982
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
8983
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
8984
if (!(geom2 && isBSplineCurve(*geom2))) {
8985
Gui::cmdAppObjectArgs(Obj,
8986
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
8988
static_cast<int>(PosId3),
8992
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {
8994
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
8995
if (!(geom1 && isBSplineCurve(*geom1))) {
8996
Gui::cmdAppObjectArgs(Obj,
8997
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
8999
static_cast<int>(PosId3),
9007
Base::Vector3d p = Obj->getPoint(GeoId3, PosId3);
9008
ActAngle = Obj->calculateAngleViaPoint(GeoId1, GeoId2, p.x, p.y);
9011
if (ActAngle < -Precision::Angular()) {
9012
std::swap(GeoId1, GeoId2);
9013
std::swap(PosId1, PosId2);
9014
ActAngle = -ActAngle;
9017
Gui::cmdAppObjectArgs(Obj,
9018
"addConstraint(Sketcher.Constraint('AngleViaPoint',%d,%d,%d,%d,%f))",
9022
static_cast<int>(PosId3),
9025
removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3);
9027
if (bothexternal || constraintCreationMode == Reference) {
9029
const std::vector<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
9031
Gui::cmdAppObjectArgs(Obj, "setDriving(%d,%s)", ConStr.size() - 1, "False");
9032
finishDatumConstraint(this, Obj, false);
9035
finishDatumConstraint(this, Obj, true);
9042
void CmdSketcherConstrainAngle::updateAction(int mode)
9047
getAction()->setIcon(
9048
Gui::BitmapFactory().iconFromTheme("Constraint_InternalAngle_Driven"));
9053
getAction()->setIcon(
9054
Gui::BitmapFactory().iconFromTheme("Constraint_InternalAngle"));
9062
class CmdSketcherConstrainEqual: public CmdSketcherConstraint
9065
CmdSketcherConstrainEqual();
9066
~CmdSketcherConstrainEqual() override
9068
const char* className() const override
9070
return "CmdSketcherConstrainEqual";
9074
void activated(int iMsg) override;
9075
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
9078
CmdSketcherConstrainEqual::CmdSketcherConstrainEqual()
9079
: CmdSketcherConstraint("Sketcher_ConstrainEqual")
9081
sAppModule = "Sketcher";
9082
sGroup = "Sketcher";
9083
sMenuText = QT_TR_NOOP("Constrain equal");
9085
QT_TR_NOOP("Create an equality constraint between two lines or between circles and arcs");
9086
sWhatsThis = "Sketcher_ConstrainEqual";
9087
sStatusTip = sToolTipText;
9088
sPixmap = "Constraint_EqualLength";
9092
allowedSelSequences = {{SelEdge, SelEdge},
9093
{SelEdge, SelExternalEdge},
9094
{SelExternalEdge, SelEdge}};
9097
void CmdSketcherConstrainEqual::activated(int iMsg)
9101
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
9104
if (selection.size() != 1
9105
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
9106
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
9107
"User parameter:BaseApp/Preferences/Mod/Sketcher");
9108
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
9110
if (constraintMode) {
9111
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
9112
getSelection().clearSelection();
9115
Gui::TranslatedUserWarning(getActiveGuiDocument()->getDocument(),
9116
QObject::tr("Wrong selection"),
9117
QObject::tr("Select two edges from the sketch."));
9123
const std::vector<std::string>& SubNames = selection[0].getSubNames();
9124
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
9128
if (SubNames.size() < 2) {
9129
Gui::TranslatedUserWarning(Obj,
9130
QObject::tr("Wrong selection"),
9131
QObject::tr("Select at least two lines from the sketch."));
9135
std::vector<int> ids;
9136
bool lineSel = false, arcSel = false, circSel = false, ellipsSel = false, arcEllipsSel = false,
9137
hasAlreadyExternal = false;
9138
bool hyperbSel = false, parabSel = false, weightSel = false;
9140
for (auto& subname : SubNames) {
9143
Sketcher::PointPos PosId;
9144
getIdsFromName(subname, Obj, GeoId, PosId);
9146
if (!isEdge(GeoId, PosId)) {
9147
Gui::TranslatedUserWarning(Obj,
9148
QObject::tr("Wrong selection"),
9149
QObject::tr("Select two or more compatible edges."));
9152
else if (GeoId == Sketcher::GeoEnum::HAxis || GeoId == Sketcher::GeoEnum::VAxis) {
9153
Gui::TranslatedUserWarning(
9155
QObject::tr("Wrong selection"),
9156
QObject::tr("Sketch axes cannot be used in equality constraints."));
9159
else if (isPointOrSegmentFixed(Obj, GeoId)) {
9161
if (hasAlreadyExternal) {
9162
showNoConstraintBetweenFixedGeometry(Obj);
9166
hasAlreadyExternal = true;
9170
const Part::Geometry* geo = Obj->getGeometry(GeoId);
9172
if (isBSplineCurve(*geo)) {
9174
Gui::TranslatedUserWarning(
9176
QObject::tr("Wrong selection"),
9177
QObject::tr("Equality for B-spline edge currently unsupported."));
9181
if (isLineSegment(*geo)) {
9184
else if (isArcOfCircle(*geo)) {
9187
else if (isCircle(*geo)) {
9188
if (isBsplinePole(geo)) {
9195
else if (isEllipse(*geo)) {
9198
else if (isArcOfEllipse(*geo)) {
9199
arcEllipsSel = true;
9201
else if (isArcOfHyperbola(*geo)) {
9204
else if (isArcOfParabola(*geo)) {
9208
Gui::TranslatedUserWarning(Obj,
9209
QObject::tr("Wrong selection"),
9210
QObject::tr("Select two or more edges of similar type."));
9214
ids.push_back(GeoId);
9219
&& ((arcSel || circSel) || (ellipsSel || arcEllipsSel) || hyperbSel || parabSel || weightSel))
9220
|| ((arcSel || circSel) && ((ellipsSel || arcEllipsSel) || hyperbSel || parabSel || weightSel))
9221
|| ((ellipsSel || arcEllipsSel) && (hyperbSel || parabSel || weightSel))
9222
|| (hyperbSel && (parabSel || weightSel)) || (parabSel && weightSel)) {
9224
Gui::TranslatedUserWarning(Obj,
9225
QObject::tr("Wrong selection"),
9226
QObject::tr("Select two or more edges of similar type."));
9231
openCommand(QT_TRANSLATE_NOOP("Command", "Add equality constraint"));
9232
for (int i = 0; i < int(ids.size() - 1); i++) {
9233
Gui::cmdAppObjectArgs(selection[0].getObject(),
9234
"addConstraint(Sketcher.Constraint('Equal',%d,%d))",
9240
tryAutoRecompute(Obj);
9243
getSelection().clearSelection();
9246
void CmdSketcherConstrainEqual::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
9248
SketcherGui::ViewProviderSketch* sketchgui =
9249
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
9250
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
9252
int GeoId1 = GeoEnum::GeoUndef, GeoId2 = GeoEnum::GeoUndef;
9259
GeoId1 = selSeq.at(0).GeoId;
9260
GeoId2 = selSeq.at(1).GeoId;
9263
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
9264
showNoConstraintBetweenFixedGeometry(Obj);
9268
const Part::Geometry* geo1 = Obj->getGeometry(GeoId1);
9269
const Part::Geometry* geo2 = Obj->getGeometry(GeoId2);
9271
if ((isLineSegment(*geo1) && ! isLineSegment(*geo2))
9272
|| (isArcOfHyperbola(*geo1) && ! isArcOfHyperbola(*geo2))
9273
|| (isArcOfParabola(*geo1) && ! isArcOfParabola(*geo2))
9274
|| (isBsplinePole(geo1) && !isBsplinePole(geo2))
9275
|| ((isCircle(*geo1) || isArcOfCircle(*geo1)) && !(isCircle(*geo2) || isArcOfCircle(*geo2)))
9276
|| ((isEllipse(*geo1) || isArcOfEllipse(*geo1)) && !(isEllipse(*geo2) || isArcOfEllipse(*geo2)))) {
9278
Gui::TranslatedUserWarning(
9280
QObject::tr("Wrong selection"),
9281
QObject::tr("Select two or more edges of similar type."));
9286
openCommand(QT_TRANSLATE_NOOP("Command", "Add equality constraint"));
9287
Gui::cmdAppObjectArgs(Obj,
9288
"addConstraint(Sketcher.Constraint('Equal',%d,%d))",
9293
tryAutoRecompute(Obj);
9304
class CmdSketcherConstrainSymmetric: public CmdSketcherConstraint
9307
CmdSketcherConstrainSymmetric();
9308
~CmdSketcherConstrainSymmetric() override
9310
const char* className() const override
9312
return "CmdSketcherConstrainSymmetric";
9316
void activated(int iMsg) override;
9317
void applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex) override;
9320
CmdSketcherConstrainSymmetric::CmdSketcherConstrainSymmetric()
9321
: CmdSketcherConstraint("Sketcher_ConstrainSymmetric")
9323
sAppModule = "Sketcher";
9324
sGroup = "Sketcher";
9325
sMenuText = QT_TR_NOOP("Constrain symmetric");
9326
sToolTipText = QT_TR_NOOP("Create a symmetry constraint "
9327
"between two points\n"
9328
"with respect to a line or a third point");
9329
sWhatsThis = "Sketcher_ConstrainSymmetric";
9330
sStatusTip = sToolTipText;
9331
sPixmap = "Constraint_Symmetric";
9335
allowedSelSequences = {{SelEdge, SelVertexOrRoot},
9336
{SelExternalEdge, SelVertex},
9337
{SelVertex, SelEdge, SelVertexOrRoot},
9338
{SelRoot, SelEdge, SelVertex},
9339
{SelVertex, SelExternalEdge, SelVertexOrRoot},
9340
{SelRoot, SelExternalEdge, SelVertex},
9341
{SelVertex, SelEdgeOrAxis, SelVertex},
9342
{SelVertex, SelVertexOrRoot, SelEdge},
9343
{SelRoot, SelVertex, SelEdge},
9344
{SelVertex, SelVertexOrRoot, SelExternalEdge},
9345
{SelRoot, SelVertex, SelExternalEdge},
9346
{SelVertex, SelVertex, SelEdgeOrAxis},
9347
{SelVertex, SelVertexOrRoot, SelVertex},
9348
{SelVertex, SelVertex, SelVertexOrRoot},
9349
{SelVertexOrRoot, SelVertex, SelVertex}};
9352
void CmdSketcherConstrainSymmetric::activated(int iMsg)
9356
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
9359
if (selection.size() != 1
9360
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
9361
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
9362
"User parameter:BaseApp/Preferences/Mod/Sketcher");
9363
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
9365
if (constraintMode) {
9366
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerGenConstraint>(this));
9367
getSelection().clearSelection();
9370
Gui::TranslatedUserWarning(
9371
getActiveGuiDocument()->getDocument(),
9372
QObject::tr("Wrong selection"),
9373
QObject::tr("Select two points and a symmetry line, "
9374
"two points and a symmetry point "
9375
"or a line and a symmetry point from the sketch."));
9381
const std::vector<std::string>& SubNames = selection[0].getSubNames();
9382
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
9384
if (SubNames.size() != 3 && SubNames.size() != 2) {
9385
Gui::TranslatedUserWarning(Obj,
9386
QObject::tr("Wrong selection"),
9387
QObject::tr("Select two points and a symmetry line, "
9388
"two points and a symmetry point "
9389
"or a line and a symmetry point from the sketch."));
9393
int GeoId1, GeoId2, GeoId3;
9394
Sketcher::PointPos PosId1, PosId2, PosId3;
9395
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
9396
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
9398
if (SubNames.size() == 2) {
9399
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
9400
showNoConstraintBetweenFixedGeometry(Obj);
9403
if (isVertex(GeoId1, PosId1) && isEdge(GeoId2, PosId2)) {
9404
std::swap(GeoId1, GeoId2);
9405
std::swap(PosId1, PosId2);
9407
if (isEdge(GeoId1, PosId1) && isVertex(GeoId2, PosId2)) {
9408
const Part::Geometry* geom = Obj->getGeometry(GeoId1);
9410
if (isLineSegment(*geom)) {
9411
if (GeoId1 == GeoId2) {
9412
Gui::TranslatedUserWarning(Obj,
9413
QObject::tr("Wrong selection"),
9414
QObject::tr("Cannot add a symmetry constraint "
9415
"between a line and its end points."));
9420
openCommand(QT_TRANSLATE_NOOP("Command", "Add symmetric constraint"));
9421
Gui::cmdAppObjectArgs(
9422
selection[0].getObject(),
9423
"addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d,%d))",
9425
static_cast<int>(Sketcher::PointPos::start),
9427
static_cast<int>(Sketcher::PointPos::end),
9429
static_cast<int>(PosId2));
9433
tryAutoRecompute(Obj);
9436
getSelection().clearSelection();
9441
Gui::TranslatedUserWarning(Obj,
9442
QObject::tr("Wrong selection"),
9443
QObject::tr("Select two points and a symmetry line, "
9444
"two points and a symmetry point "
9445
"or a line and a symmetry point from the sketch."));
9449
getIdsFromName(SubNames[2], Obj, GeoId3, PosId3);
9451
if (isEdge(GeoId1, PosId1) && isVertex(GeoId3, PosId3)) {
9452
std::swap(GeoId1, GeoId3);
9453
std::swap(PosId1, PosId3);
9455
else if (isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
9456
std::swap(GeoId2, GeoId3);
9457
std::swap(PosId2, PosId3);
9460
if (areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3)) {
9461
showNoConstraintBetweenFixedGeometry(Obj);
9465
if (isVertex(GeoId1, PosId1) && isVertex(GeoId2, PosId2)) {
9466
if (isEdge(GeoId3, PosId3)) {
9467
const Part::Geometry* geom = Obj->getGeometry(GeoId3);
9469
if (isLineSegment(*geom)) {
9470
if (GeoId1 == GeoId2 && GeoId2 == GeoId3) {
9471
Gui::TranslatedUserWarning(Obj,
9472
QObject::tr("Wrong selection"),
9473
QObject::tr("Cannot add a symmetry constraint "
9474
"between a line and its end points!"));
9479
openCommand(QT_TRANSLATE_NOOP("Command", "Add symmetric constraint"));
9480
Gui::cmdAppObjectArgs(
9481
selection[0].getObject(),
9482
"addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d))",
9484
static_cast<int>(PosId1),
9486
static_cast<int>(PosId2),
9491
tryAutoRecompute(Obj);
9494
getSelection().clearSelection();
9498
else if (isVertex(GeoId3, PosId3)) {
9500
openCommand(QT_TRANSLATE_NOOP("Command", "Add symmetric constraint"));
9501
Gui::cmdAppObjectArgs(
9502
selection[0].getObject(),
9503
"addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d,%d))",
9505
static_cast<int>(PosId1),
9507
static_cast<int>(PosId2),
9509
static_cast<int>(PosId3));
9513
tryAutoRecompute(Obj);
9516
getSelection().clearSelection();
9521
Gui::TranslatedUserWarning(Obj,
9522
QObject::tr("Wrong selection"),
9523
QObject::tr("Select two points and a symmetry line, "
9524
"two points and a symmetry point "
9525
"or a line and a symmetry point from the sketch."));
9528
void CmdSketcherConstrainSymmetric::applyConstraint(std::vector<SelIdPair>& selSeq, int seqIndex)
9530
SketcherGui::ViewProviderSketch* sketchgui =
9531
static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
9532
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
9534
int GeoId1 = GeoEnum::GeoUndef, GeoId2 = GeoEnum::GeoUndef, GeoId3 = GeoEnum::GeoUndef;
9535
Sketcher::PointPos PosId1 = Sketcher::PointPos::none, PosId2 = Sketcher::PointPos::none,
9536
PosId3 = Sketcher::PointPos::none;
9542
GeoId1 = GeoId2 = selSeq.at(0).GeoId;
9543
GeoId3 = selSeq.at(1).GeoId;
9544
PosId1 = Sketcher::PointPos::start;
9545
PosId2 = Sketcher::PointPos::end;
9546
PosId3 = selSeq.at(1).PosId;
9548
if (GeoId1 == GeoId3) {
9549
Gui::TranslatedUserWarning(
9551
QObject::tr("Wrong selection"),
9553
"Cannot add a symmetry constraint between a line and its end points!"));
9557
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) {
9558
showNoConstraintBetweenFixedGeometry(Obj);
9574
GeoId1 = selSeq.at(0).GeoId;
9575
GeoId2 = selSeq.at(2).GeoId;
9576
GeoId3 = selSeq.at(1).GeoId;
9577
PosId1 = selSeq.at(0).PosId;
9578
PosId2 = selSeq.at(2).PosId;
9579
PosId3 = selSeq.at(1).PosId;
9581
if (isEdge(GeoId1, PosId1) && isVertex(GeoId3, PosId3)) {
9582
std::swap(GeoId1, GeoId3);
9583
std::swap(PosId1, PosId3);
9585
else if (isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
9586
std::swap(GeoId2, GeoId3);
9587
std::swap(PosId2, PosId3);
9590
if (areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3)) {
9591
showNoConstraintBetweenFixedGeometry(Obj);
9595
const Part::Geometry* geom = Obj->getGeometry(GeoId3);
9597
if (isLineSegment(*geom)) {
9598
if (GeoId1 == GeoId2 && GeoId2 == GeoId3) {
9599
Gui::TranslatedUserWarning(Obj,
9600
QObject::tr("Wrong selection"),
9601
QObject::tr("Cannot add a symmetry constraint "
9602
"between a line and its end points."));
9607
openCommand(QT_TRANSLATE_NOOP("Command", "Add symmetric constraint"));
9608
Gui::cmdAppObjectArgs(
9610
"addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d))",
9612
static_cast<int>(PosId1),
9614
static_cast<int>(PosId2),
9619
tryAutoRecompute(Obj);
9622
Gui::TranslatedUserWarning(
9624
QObject::tr("Wrong selection"),
9625
QObject::tr("Select two points and a symmetry line, "
9626
"two points and a symmetry point "
9627
"or a line and a symmetry point from the sketch."));
9635
GeoId1 = selSeq.at(0).GeoId;
9636
GeoId2 = selSeq.at(1).GeoId;
9637
GeoId3 = selSeq.at(2).GeoId;
9638
PosId1 = selSeq.at(0).PosId;
9639
PosId2 = selSeq.at(1).PosId;
9640
PosId3 = selSeq.at(2).PosId;
9642
if (areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3)) {
9643
showNoConstraintBetweenFixedGeometry(Obj);
9653
openCommand(QT_TRANSLATE_NOOP("Command", "Add symmetric constraint"));
9654
Gui::cmdAppObjectArgs(Obj,
9655
"addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d,%d))",
9657
static_cast<int>(PosId1),
9659
static_cast<int>(PosId2),
9661
static_cast<int>(PosId3));
9666
tryAutoRecompute(Obj);
9669
getSelection().clearSelection();
9675
DEF_STD_CMD_A(CmdSketcherConstrainSnellsLaw)
9677
CmdSketcherConstrainSnellsLaw::CmdSketcherConstrainSnellsLaw()
9678
: Command("Sketcher_ConstrainSnellsLaw")
9680
sAppModule = "Sketcher";
9681
sGroup = "Sketcher";
9682
sMenuText = QT_TR_NOOP("Constrain refraction (Snell's law)");
9683
sToolTipText = QT_TR_NOOP("Create a refraction law (Snell's law)"
9684
"constraint between two endpoints of rays\n"
9685
"and an edge as an interface.");
9686
sWhatsThis = "Sketcher_ConstrainSnellsLaw";
9687
sStatusTip = sToolTipText;
9688
sPixmap = "Constraint_SnellsLaw";
9693
void CmdSketcherConstrainSnellsLaw::activated(int iMsg)
9698
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
9701
if (selection.size() != 1
9702
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
9703
QString strHelp = QObject::tr("Select two endpoints of lines to act as rays, "
9704
"and an edge representing a boundary. "
9705
"The first selected point corresponds "
9706
"to index n1, second to n2, "
9707
"and the value sets the ratio n2/n1.",
9708
"Constraint_SnellsLaw");
9710
const char dmbg[] = "Constraint_SnellsLaw";
9712
QString strError = QObject::tr("Selected objects are not just geometry "
9716
strError.append(strHelp);
9717
Gui::TranslatedUserWarning(getActiveGuiDocument()->getDocument(),
9718
QObject::tr("Wrong selection"),
9719
std::move(strError));
9723
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
9724
const std::vector<std::string>& SubNames = selection[0].getSubNames();
9726
if (SubNames.size() != 3) {
9727
Gui::TranslatedUserWarning(Obj,
9728
QObject::tr("Wrong selection"),
9729
QObject::tr("Number of selected objects is not 3"));
9733
int GeoId1, GeoId2, GeoId3;
9734
Sketcher::PointPos PosId1, PosId2, PosId3;
9735
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
9736
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
9737
getIdsFromName(SubNames[2], Obj, GeoId3, PosId3);
9740
if (isEdge(GeoId1, PosId1)) {
9741
std::swap(GeoId1, GeoId2);
9742
std::swap(PosId1, PosId2);
9744
if (isEdge(GeoId2, PosId2)) {
9745
std::swap(GeoId2, GeoId3);
9746
std::swap(PosId2, PosId3);
9750
if (areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3)) {
9751
Gui::TranslatedUserWarning(
9753
QObject::tr("Wrong selection"),
9754
QObject::tr("Cannot create constraint with external geometry only."));
9758
if (!(isVertex(GeoId1, PosId1) && !isSimpleVertex(Obj, GeoId1, PosId1)
9759
&& isVertex(GeoId2, PosId2) && !isSimpleVertex(Obj, GeoId2, PosId2)
9760
&& isEdge(GeoId3, PosId3))) {
9762
Gui::TranslatedUserWarning(Obj,
9763
QObject::tr("Wrong selection"),
9764
QObject::tr("Incompatible geometry is selected."));
9768
const Part::Geometry* geo = Obj->getGeometry(GeoId3);
9779
if (isBsplinePole(geo)) {
9780
Gui::TranslatedUserWarning(Obj,
9781
QObject::tr("Wrong selection"),
9782
QObject::tr("Select an edge that is not a B-spline weight."));
9790
QDialog dlg(Gui::getMainWindow());
9791
Ui::InsertDatum ui_Datum;
9792
ui_Datum.setupUi(&dlg);
9793
dlg.setWindowTitle(EditDatumDialog::tr("Refractive index ratio"));
9794
ui_Datum.label->setText(EditDatumDialog::tr("Ratio n2/n1:"));
9795
Base::Quantity init_val;
9796
init_val.setUnit(Base::Unit());
9797
init_val.setValue(0.0);
9799
ui_Datum.labelEdit->setValue(init_val);
9800
ui_Datum.labelEdit->setParamGrpPath(
9801
QByteArray("User parameter:BaseApp/History/SketcherRefrIndexRatio"));
9802
ui_Datum.labelEdit->setEntryName(QByteArray("DatumValue"));
9803
ui_Datum.labelEdit->setToLastUsedValue();
9804
ui_Datum.labelEdit->selectNumber();
9805
ui_Datum.labelEdit->setSingleStep(0.05);
9808
if (dlg.exec() != QDialog::Accepted) {
9811
ui_Datum.labelEdit->pushToHistory();
9813
Base::Quantity newQuant = ui_Datum.labelEdit->value();
9814
n2divn1 = newQuant.getValue();
9817
openCommand(QT_TRANSLATE_NOOP("Command", "Add Snell's law constraint"));
9819
bool safe = addConstraintSafely(Obj, [&]() {
9820
if (!IsPointAlreadyOnCurve(GeoId2, GeoId1, PosId1, Obj)) {
9821
Gui::cmdAppObjectArgs(selection[0].getObject(),
9822
"addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d))",
9824
static_cast<int>(PosId1),
9826
static_cast<int>(PosId2));
9829
if (!IsPointAlreadyOnCurve(GeoId3, GeoId1, PosId1, Obj)) {
9830
Gui::cmdAppObjectArgs(selection[0].getObject(),
9831
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
9833
static_cast<int>(PosId1),
9837
Gui::cmdAppObjectArgs(
9838
selection[0].getObject(),
9839
"addConstraint(Sketcher.Constraint('SnellsLaw',%d,%d,%d,%d,%d,%.12f))",
9841
static_cast<int>(PosId1),
9843
static_cast<int>(PosId2),
9861
tryAutoRecompute(Obj);
9865
getSelection().clearSelection();
9868
bool CmdSketcherConstrainSnellsLaw::isActive()
9870
return isCreateConstraintActive(getActiveGuiDocument());
9874
DEF_STD_CMD_A(CmdSketcherChangeDimensionConstraint)
9876
CmdSketcherChangeDimensionConstraint::CmdSketcherChangeDimensionConstraint()
9877
: Command("Sketcher_ChangeDimensionConstraint")
9879
sAppModule = "Sketcher";
9880
sGroup = "Sketcher";
9881
sMenuText = QT_TR_NOOP("Change value");
9882
sToolTipText = QT_TR_NOOP("Change the value of a dimensional constraint");
9883
sWhatsThis = "Sketcher_ChangeDimensionConstraint";
9884
sStatusTip = sToolTipText;
9888
void CmdSketcherChangeDimensionConstraint::activated(int iMsg)
9892
auto getDimConstraint = []() {
9893
std::vector<Gui::SelectionObject> selection{getSelection().getSelectionEx()};
9894
if (selection.size() != 1 || selection[0].getSubNames().size() != 1) {
9895
throw Base::RuntimeError();
9898
if (auto sketch = dynamic_cast<Sketcher::SketchObject*>(selection[0].getObject())) {
9899
std::string subName = selection[0].getSubNames().at(0);
9900
if (subName.size() > 10 && subName.substr(0, 10) == "Constraint") {
9901
int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(subName);
9902
return std::make_tuple(sketch, ConstrId);
9906
throw Base::RuntimeError();
9910
auto value = getDimConstraint();
9911
EditDatumDialog editDatumDialog(std::get<0>(value), std::get<1>(value));
9912
editDatumDialog.exec(false);
9914
catch (const Base::RuntimeError&) {
9915
Gui::TranslatedUserWarning(getActiveGuiDocument()->getDocument(),
9916
QObject::tr("Wrong selection"),
9917
QObject::tr("Select one dimensional constraint from the sketch."));
9921
bool CmdSketcherChangeDimensionConstraint::isActive()
9923
return isCommandActive(getActiveGuiDocument());
9928
DEF_STD_CMD_AU(CmdSketcherToggleDrivingConstraint)
9930
CmdSketcherToggleDrivingConstraint::CmdSketcherToggleDrivingConstraint()
9931
: Command("Sketcher_ToggleDrivingConstraint")
9933
sAppModule = "Sketcher";
9934
sGroup = "Sketcher";
9935
sMenuText = QT_TR_NOOP("Toggle driving/reference constraint");
9936
sToolTipText = QT_TR_NOOP("Set the toolbar, "
9937
"or the selected constraints,\n"
9938
"into driving or reference mode");
9939
sWhatsThis = "Sketcher_ToggleDrivingConstraint";
9940
sStatusTip = sToolTipText;
9941
sPixmap = "Sketcher_ToggleConstraint";
9946
Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
9947
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainLock");
9948
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainDistance");
9949
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainDistanceX");
9950
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainDistanceY");
9951
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainRadius");
9952
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainDiameter");
9953
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainRadiam");
9954
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainAngle");
9955
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_CompConstrainRadDia");
9956
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_Dimension");
9957
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_CompDimensionTools");
9958
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ToggleDrivingConstraint");
9962
void CmdSketcherToggleDrivingConstraint::updateAction(int mode)
9964
auto act = getAction();
9966
switch (static_cast<ConstraintCreationMode>(mode)) {
9967
case ConstraintCreationMode::Driving:
9968
act->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_ToggleConstraint"));
9970
case ConstraintCreationMode::Reference:
9971
act->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_ToggleConstraint_Driven"));
9977
void CmdSketcherToggleDrivingConstraint::activated(int iMsg)
9980
bool modeChange = true;
9982
std::vector<Gui::SelectionObject> selection;
9984
if (Gui::Selection().countObjectsOfType(Sketcher::SketchObject::getClassTypeId()) > 0) {
9988
selection = getSelection().getSelectionEx();
9991
if (selection.size() != 1
9992
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
9993
Gui::TranslatedUserWarning(getActiveGuiDocument()->getDocument(),
9994
QObject::tr("Wrong selection"),
9995
QObject::tr("Select constraints from the sketch."));
9999
Sketcher::SketchObject* Obj =
10000
static_cast<Sketcher::SketchObject*>(selection[0].getObject());
10003
const std::vector<std::string>& SubNames = selection[0].getSubNames();
10004
if (SubNames.empty()) {
10005
Gui::TranslatedUserWarning(Obj,
10006
QObject::tr("Wrong selection"),
10007
QObject::tr("Select constraints from the sketch."));
10011
for (auto& subname : SubNames) {
10013
if (subname.size() > 10 && subname.substr(0, 10) == "Constraint") {
10014
modeChange = false;
10021
Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
10023
if (constraintCreationMode == Driving) {
10024
constraintCreationMode = Reference;
10027
constraintCreationMode = Driving;
10030
rcCmdMgr.updateCommands("ToggleDrivingConstraint",
10031
static_cast<int>(constraintCreationMode));
10035
Sketcher::SketchObject* Obj =
10036
static_cast<Sketcher::SketchObject*>(selection[0].getObject());
10039
const std::vector<std::string>& SubNames = selection[0].getSubNames();
10040
if (SubNames.empty()) {
10041
Gui::TranslatedUserWarning(Obj,
10042
QObject::tr("Wrong selection"),
10043
QObject::tr("Select constraints from the sketch."));
10048
openCommand(QT_TRANSLATE_NOOP("Command", "Toggle constraint to driving/reference"));
10050
int successful = SubNames.size();
10052
for (auto& subname : SubNames) {
10054
if (subname.size() > 10 && subname.substr(0, 10) == "Constraint") {
10055
int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(subname);
10058
Gui::cmdAppObjectArgs(selection[0].getObject(), "toggleDriving(%d)", ConstrId);
10060
catch (const Base::Exception&) {
10066
if (successful > 0) {
10073
tryAutoRecompute(Obj);
10076
getSelection().clearSelection();
10080
bool CmdSketcherToggleDrivingConstraint::isActive()
10082
return isCommandActive(getActiveGuiDocument());
10085
DEF_STD_CMD_A(CmdSketcherToggleActiveConstraint)
10087
CmdSketcherToggleActiveConstraint::CmdSketcherToggleActiveConstraint()
10088
: Command("Sketcher_ToggleActiveConstraint")
10090
sAppModule = "Sketcher";
10091
sGroup = "Sketcher";
10092
sMenuText = QT_TR_NOOP("Activate/deactivate constraint");
10093
sToolTipText = QT_TR_NOOP("Activates or deactivates "
10094
"the selected constraints");
10095
sWhatsThis = "Sketcher_ToggleActiveConstraint";
10096
sStatusTip = sToolTipText;
10097
sPixmap = "Sketcher_ToggleActiveConstraint";
10102
void CmdSketcherToggleActiveConstraint::activated(int iMsg)
10106
std::vector<Gui::SelectionObject> selection;
10108
if (Gui::Selection().countObjectsOfType(Sketcher::SketchObject::getClassTypeId()) > 0) {
10112
selection = getSelection().getSelectionEx();
10115
if (selection.size() != 1
10116
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
10117
Gui::TranslatedUserWarning(getActiveGuiDocument()->getDocument(),
10118
QObject::tr("Wrong selection"),
10119
QObject::tr("Select constraints from the sketch."));
10123
Sketcher::SketchObject* Obj =
10124
static_cast<Sketcher::SketchObject*>(selection[0].getObject());
10127
const std::vector<std::string>& SubNames = selection[0].getSubNames();
10128
if (SubNames.empty()) {
10129
Gui::TranslatedUserWarning(Obj,
10130
QObject::tr("Wrong selection"),
10131
QObject::tr("Select constraints from the sketch."));
10136
openCommand(QT_TRANSLATE_NOOP("Command", "Activate/Deactivate constraints"));
10138
int successful = SubNames.size();
10140
for (auto& subname : SubNames) {
10142
if (subname.size() > 10 && subname.substr(0, 10) == "Constraint") {
10143
int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(subname);
10146
Gui::cmdAppObjectArgs(selection[0].getObject(), "toggleActive(%d)", ConstrId);
10148
catch (const Base::Exception&) {
10154
if (successful > 0) {
10161
tryAutoRecompute(Obj);
10164
getSelection().clearSelection();
10168
bool CmdSketcherToggleActiveConstraint::isActive()
10170
return isCreateConstraintActive(getActiveGuiDocument());
10173
void CreateSketcherCommandsConstraints()
10175
Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
10177
rcCmdMgr.addCommand(new CmdSketcherConstrainHorizontal());
10178
rcCmdMgr.addCommand(new CmdSketcherConstrainVertical());
10179
rcCmdMgr.addCommand(new CmdSketcherConstrainHorVer());
10180
rcCmdMgr.addCommand(new CmdSketcherCompHorizontalVertical());
10181
rcCmdMgr.addCommand(new CmdSketcherConstrainLock());
10182
rcCmdMgr.addCommand(new CmdSketcherConstrainBlock());
10183
rcCmdMgr.addCommand(new CmdSketcherConstrainCoincidentUnified());
10184
rcCmdMgr.addCommand(new CmdSketcherConstrainCoincident());
10185
rcCmdMgr.addCommand(new CmdSketcherDimension());
10186
rcCmdMgr.addCommand(new CmdSketcherConstrainParallel());
10187
rcCmdMgr.addCommand(new CmdSketcherConstrainPerpendicular());
10188
rcCmdMgr.addCommand(new CmdSketcherConstrainTangent());
10189
rcCmdMgr.addCommand(new CmdSketcherConstrainDistance());
10190
rcCmdMgr.addCommand(new CmdSketcherConstrainDistanceX());
10191
rcCmdMgr.addCommand(new CmdSketcherConstrainDistanceY());
10192
rcCmdMgr.addCommand(new CmdSketcherConstrainRadius());
10193
rcCmdMgr.addCommand(new CmdSketcherConstrainDiameter());
10194
rcCmdMgr.addCommand(new CmdSketcherConstrainRadiam());
10195
rcCmdMgr.addCommand(new CmdSketcherCompConstrainRadDia());
10196
rcCmdMgr.addCommand(new CmdSketcherConstrainAngle());
10197
rcCmdMgr.addCommand(new CmdSketcherConstrainEqual());
10198
rcCmdMgr.addCommand(new CmdSketcherConstrainPointOnObject());
10199
rcCmdMgr.addCommand(new CmdSketcherConstrainSymmetric());
10200
rcCmdMgr.addCommand(new CmdSketcherConstrainSnellsLaw());
10201
rcCmdMgr.addCommand(new CmdSketcherChangeDimensionConstraint());
10202
rcCmdMgr.addCommand(new CmdSketcherToggleDrivingConstraint());
10203
rcCmdMgr.addCommand(new CmdSketcherToggleActiveConstraint());
10204
rcCmdMgr.addCommand(new CmdSketcherCompDimensionTools());
10205
rcCmdMgr.addCommand(new CmdSketcherCompConstrainTools());
10206
rcCmdMgr.addCommand(new CmdSketcherCompToggleConstraints());