FreeCAD

Форк
0
/
Utils.cpp 
879 строк · 31.3 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2021 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com>     *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This library is free software; you can redistribute it and/or         *
7
 *   modify it under the terms of the GNU Library General Public           *
8
 *   License as published by the Free Software Foundation; either          *
9
 *   version 2 of the License, or (at your option) any later version.      *
10
 *                                                                         *
11
 *   This library  is distributed in the hope that it will be useful,      *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU Library General Public License for more details.                  *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Library General Public     *
17
 *   License along with this library; see the file COPYING.LIB. If not,    *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24
#ifndef _PreComp_
25
#include <cfloat>
26

27
#include <QCursor>
28
#include <QLocale>
29
#include <QRegularExpression>
30
#endif
31

32
#include <App/Application.h>
33
#include <Base/Quantity.h>
34
#include <Base/Tools.h>
35
#include <Base/UnitsApi.h>
36
#include <Gui/CommandT.h>
37
#include <Gui/Document.h>
38
#include <Gui/Selection.h>
39
#include <Mod/Sketcher/App/GeometryFacade.h>
40
#include <Mod/Sketcher/App/SketchObject.h>
41

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

46

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

51
bool Sketcher::isCircle(const Part::Geometry& geom)
52
{
53
    return geom.is<Part::GeomCircle>();
54
}
55

56
bool Sketcher::isArcOfCircle(const Part::Geometry& geom)
57
{
58
    return geom.is<Part::GeomArcOfCircle>();
59
}
60

61
bool Sketcher::isEllipse(const Part::Geometry& geom)
62
{
63
    return geom.is<Part::GeomEllipse>();
64
}
65

66
bool Sketcher::isArcOfEllipse(const Part::Geometry& geom)
67
{
68
    return geom.is<Part::GeomArcOfEllipse>();
69
}
70

71
bool Sketcher::isLineSegment(const Part::Geometry& geom)
72
{
73
    return geom.is<Part::GeomLineSegment>();
74
}
75

76
bool Sketcher::isArcOfHyperbola(const Part::Geometry& geom)
77
{
78
    return geom.is<Part::GeomArcOfHyperbola>();
79
}
80

81
bool Sketcher::isArcOfParabola(const Part::Geometry& geom)
82
{
83
    return geom.is<Part::GeomArcOfParabola>();
84
}
85

86
bool Sketcher::isBSplineCurve(const Part::Geometry& geom)
87
{
88
    return geom.is<Part::GeomBSplineCurve>();
89
}
90

91
bool Sketcher::isPeriodicBSplineCurve(const Part::Geometry& geom)
92
{
93
    if (geom.is<Part::GeomBSplineCurve>()) {
94
        auto* spline = static_cast<const Part::GeomBSplineCurve*>(&geom);
95
        return spline->isPeriodic();
96
    }
97
    return false;
98
}
99

100
bool Sketcher::isPoint(const Part::Geometry& geom)
101
{
102
    return geom.is<Part::GeomPoint>();
103
}
104

105
bool Sketcher::isCircleOrArc(const Part::Geometry& geo)
106
{
107
    return isCircle(geo) || isArcOfCircle(geo);
108
};
109

110
std::tuple<double, Base::Vector3d> Sketcher::getRadiusCenterCircleArc(const Part::Geometry* geo)
111
{
112
    if (isArcOfCircle(*geo)) {
113
        auto arc = static_cast<const Part::GeomArcOfCircle*>(geo);  // NOLINT
114
        return std::tuple<double, Base::Vector3d>(arc->getRadius(), arc->getCenter());
115
    }
116
    else if (isCircle(*geo)) {
117
        auto circ = static_cast<const Part::GeomCircle*>(geo);  // NOLINT
118
        return std::tuple<double, Base::Vector3d>(circ->getRadius(), circ->getCenter());
119
    }
120

121
    THROWM(Base::TypeError, "getRadiusCenterCircleArc - Neither an arc nor a circle")
122
};
123

124
bool SketcherGui::tryAutoRecompute(Sketcher::SketchObject* obj, bool& autoremoveredundants)
125
{
126
    ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
127
        "User parameter:BaseApp/Preferences/Mod/Sketcher");
128
    bool autoRecompute = hGrp->GetBool("AutoRecompute", false);
129
    bool autoRemoveRedundants = hGrp->GetBool("AutoRemoveRedundants", false);
130

131
    // We need to make sure the solver has right redundancy information before trying to remove the
132
    // redundants. for example if a non-driving constraint has been added.
133
    if (autoRemoveRedundants && autoRecompute) {
134
        obj->solve();
135
    }
136

137
    if (autoRemoveRedundants) {
138
        obj->autoRemoveRedundants();
139
    }
140

141
    if (autoRecompute) {
142
        Gui::Command::updateActive();
143
    }
144

145
    autoremoveredundants = autoRemoveRedundants;
146

147
    return autoRecompute;
148
}
149

150
bool SketcherGui::tryAutoRecompute(Sketcher::SketchObject* obj)
151
{
152
    bool autoremoveredundants;
153

154
    return tryAutoRecompute(obj, autoremoveredundants);
155
}
156

157
void SketcherGui::tryAutoRecomputeIfNotSolve(Sketcher::SketchObject* obj)
158
{
159
    bool autoremoveredundants;
160

161
    if (!tryAutoRecompute(obj, autoremoveredundants)) {
162
        obj->solve();
163

164
        if (autoremoveredundants) {
165
            obj->autoRemoveRedundants();
166
        }
167
    }
168
}
169

170
std::string SketcherGui::getStrippedPythonExceptionString(const Base::Exception& e)
171
{
172
    std::string msg = e.what();
173

174
    if (msg.length() > 26 && msg.substr(0, 26) == "FreeCAD exception thrown (") {
175
        return msg.substr(26, msg.length() - 27);
176
    }
177
    else {
178
        return msg;
179
    }
180
}
181

182
bool SketcherGui::ReleaseHandler(Gui::Document* doc)
183
{
184
    if (doc) {
185
        if (doc->getInEdit()
186
            && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
187
            SketcherGui::ViewProviderSketch* vp =
188
                static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
189

190
            if (static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit())->getSketchMode()
191
                == ViewProviderSketch::STATUS_SKETCH_UseHandler) {
192

193
                vp->purgeHandler();
194
                return true;
195
            }
196
        }
197
    }
198
    return false;
199
}
200

201
void SketcherGui::getIdsFromName(const std::string& name,
202
                                 const Sketcher::SketchObject* Obj,
203
                                 int& GeoId,
204
                                 PointPos& PosId)
205
{
206
    GeoId = GeoEnum::GeoUndef;
207
    PosId = Sketcher::PointPos::none;
208

209
    if (name.size() > 4 && name.substr(0, 4) == "Edge") {
210
        GeoId = std::atoi(name.substr(4, 4000).c_str()) - 1;
211
    }
212
    else if (name.size() == 9 && name.substr(0, 9) == "RootPoint") {
213
        GeoId = Sketcher::GeoEnum::RtPnt;
214
        PosId = Sketcher::PointPos::start;
215
    }
216
    else if (name.size() == 6 && name.substr(0, 6) == "H_Axis") {
217
        GeoId = Sketcher::GeoEnum::HAxis;
218
    }
219
    else if (name.size() == 6 && name.substr(0, 6) == "V_Axis") {
220
        GeoId = Sketcher::GeoEnum::VAxis;
221
    }
222
    else if (name.size() > 12 && name.substr(0, 12) == "ExternalEdge") {
223
        GeoId = Sketcher::GeoEnum::RefExt + 1 - std::atoi(name.substr(12, 4000).c_str());
224
    }
225
    else if (name.size() > 6 && name.substr(0, 6) == "Vertex") {
226
        int VtId = std::atoi(name.substr(6, 4000).c_str()) - 1;
227
        Obj->getGeoVertexIndex(VtId, GeoId, PosId);
228
    }
229
}
230

231
std::vector<int> SketcherGui::getGeoIdsOfEdgesFromNames(const Sketcher::SketchObject* Obj,
232
                                                        const std::vector<std::string>& names)
233
{
234
    std::vector<int> geoids;
235

236
    for (const auto& name : names) {
237
        if (name.size() > 4 && name.substr(0, 4) == "Edge") {
238
            geoids.push_back(std::atoi(name.substr(4, 4000).c_str()) - 1);
239
        }
240
        else if (name.size() > 12 && name.substr(0, 12) == "ExternalEdge") {
241
            geoids.push_back(Sketcher::GeoEnum::RefExt + 1
242
                             - std::atoi(name.substr(12, 4000).c_str()));
243
        }
244
        else if (name.size() > 6 && name.substr(0, 6) == "Vertex") {
245
            int VtId = std::atoi(name.substr(6, 4000).c_str()) - 1;
246
            int GeoId;
247
            Sketcher::PointPos PosId;
248
            Obj->getGeoVertexIndex(VtId, GeoId, PosId);
249
            const Part::Geometry* geo = Obj->getGeometry(GeoId);
250
            if (geo->is<Part::GeomPoint>()) {
251
                geoids.push_back(GeoId);
252
            }
253
        }
254
    }
255

256
    return geoids;
257
}
258

259
bool SketcherGui::checkBothExternal(int GeoId1, int GeoId2)
260
{
261
    if (GeoId1 == GeoEnum::GeoUndef || GeoId2 == GeoEnum::GeoUndef) {
262
        return false;
263
    }
264
    else {
265
        return (GeoId1 < 0 && GeoId2 < 0);
266
    }
267
}
268

269
bool SketcherGui::isPointOrSegmentFixed(const Sketcher::SketchObject* Obj, int GeoId)
270
{
271
    const std::vector<Sketcher::Constraint*>& vals = Obj->Constraints.getValues();
272

273
    if (GeoId == GeoEnum::GeoUndef) {
274
        return false;
275
    }
276
    else {
277
        return checkConstraint(vals, Sketcher::Block, GeoId, Sketcher::PointPos::none)
278
            || GeoId <= Sketcher::GeoEnum::RtPnt;
279
    }
280
}
281

282
bool SketcherGui::areBothPointsOrSegmentsFixed(const Sketcher::SketchObject* Obj,
283
                                               int GeoId1,
284
                                               int GeoId2)
285
{
286
    const std::vector<Sketcher::Constraint*>& vals = Obj->Constraints.getValues();
287

288
    if (GeoId1 == GeoEnum::GeoUndef || GeoId2 == GeoEnum::GeoUndef) {
289
        return false;
290
    }
291
    else {
292
        return ((checkConstraint(vals, Sketcher::Block, GeoId1, Sketcher::PointPos::none)
293
                 || GeoId1 <= Sketcher::GeoEnum::RtPnt)
294
                && (checkConstraint(vals, Sketcher::Block, GeoId2, Sketcher::PointPos::none)
295
                    || GeoId2 <= Sketcher::GeoEnum::RtPnt));
296
    }
297
}
298

299
bool SketcherGui::areAllPointsOrSegmentsFixed(const Sketcher::SketchObject* Obj,
300
                                              int GeoId1,
301
                                              int GeoId2,
302
                                              int GeoId3)
303
{
304
    const std::vector<Sketcher::Constraint*>& vals = Obj->Constraints.getValues();
305

306
    if (GeoId1 == GeoEnum::GeoUndef || GeoId2 == GeoEnum::GeoUndef || GeoId3 == GeoEnum::GeoUndef) {
307
        return false;
308
    }
309
    else {
310
        return ((checkConstraint(vals, Sketcher::Block, GeoId1, Sketcher::PointPos::none)
311
                 || GeoId1 <= Sketcher::GeoEnum::RtPnt)
312
                && (checkConstraint(vals, Sketcher::Block, GeoId2, Sketcher::PointPos::none)
313
                    || GeoId2 <= Sketcher::GeoEnum::RtPnt)
314
                && (checkConstraint(vals, Sketcher::Block, GeoId3, Sketcher::PointPos::none)
315
                    || GeoId3 <= Sketcher::GeoEnum::RtPnt));
316
    }
317
}
318

319
bool SketcherGui::isSimpleVertex(const Sketcher::SketchObject* Obj, int GeoId, PointPos PosId)
320
{
321
    if (PosId == Sketcher::PointPos::start
322
        && (GeoId == Sketcher::GeoEnum::HAxis || GeoId == Sketcher::GeoEnum::VAxis)) {
323
        return true;
324
    }
325
    const Part::Geometry* geo = Obj->getGeometry(GeoId);
326
    if (geo->is<Part::GeomPoint>()) {
327
        return true;
328
    }
329
    else if (PosId == Sketcher::PointPos::mid) {
330
        return true;
331
    }
332
    else {
333
        return false;
334
    }
335
}
336

337
bool SketcherGui::isBsplineKnot(const Sketcher::SketchObject* Obj, int GeoId)
338
{
339
    auto gf = Obj->getGeometryFacade(GeoId);
340
    return (gf && gf->getInternalType() == Sketcher::InternalType::BSplineKnotPoint);
341
}
342

343
bool SketcherGui::isBsplineKnotOrEndPoint(const Sketcher::SketchObject* Obj,
344
                                          int GeoId,
345
                                          Sketcher::PointPos PosId)
346
{
347
    // check first using geometry facade
348
    if (isBsplineKnot(Obj, GeoId)) {
349
        return true;
350
    }
351

352
    const Part::Geometry* geo = Obj->getGeometry(GeoId);
353
    // end points of B-Splines are also knots
354
    if (geo->is<Part::GeomBSplineCurve>()
355
        && (PosId == Sketcher::PointPos::start || PosId == Sketcher::PointPos::end)) {
356
        return true;
357
    }
358

359
    return false;
360
}
361

362
bool SketcherGui::IsPointAlreadyOnCurve(int GeoIdCurve,
363
                                        int GeoIdPoint,
364
                                        Sketcher::PointPos PosIdPoint,
365
                                        Sketcher::SketchObject* Obj)
366
{
367
    // This func is a "smartness" behind three-element tangent-, perp.- and angle-via-point.
368
    // We want to find out, if the point supplied by user is already on
369
    //  both of the curves. If not, necessary point-on-object constraints
370
    //  are to be added automatically.
371
    // Simple geometric test seems to be the best, because a point can be
372
    //  constrained to a curve in a number of ways (e.g. it is an endpoint of an
373
    //  arc, or is coincident to endpoint of an arc, or it is an endpoint of an
374
    //  ellipse's major diameter line). Testing all those possibilities is way
375
    //  too much trouble, IMO(DeepSOIC).
376
    //  One exception: check for knots on their B-splines, at least until point on B-spline is
377
    //  implemented. (Ajinkya)
378
    if (isBsplineKnot(Obj, GeoIdPoint)) {
379
        const Part::Geometry* geoCurve = Obj->getGeometry(GeoIdCurve);
380
        if (geoCurve->is<Part::GeomBSplineCurve>()) {
381
            const std::vector<Constraint*>& constraints = Obj->Constraints.getValues();
382
            for (const auto& constraint : constraints) {
383
                if (constraint->Type == Sketcher::ConstraintType::InternalAlignment
384
                    && constraint->First == GeoIdPoint && constraint->Second == GeoIdCurve) {
385
                    return true;
386
                }
387
            }
388
        }
389
    }
390

391
    Base::Vector3d p = Obj->getPoint(GeoIdPoint, PosIdPoint);
392
    return Obj->isPointOnCurve(GeoIdCurve, p.x, p.y);
393
}
394

395
bool SketcherGui::isBsplinePole(const Part::Geometry* geo)
396
{
397
    auto gf = GeometryFacade::getFacade(geo);
398

399
    if (gf) {
400
        return gf->getInternalType() == InternalType::BSplineControlPoint;
401
    }
402

403
    THROWM(Base::ValueError, "Null geometry in isBsplinePole - please report")
404
}
405

406
bool SketcherGui::isBsplinePole(const Sketcher::SketchObject* Obj, int GeoId)
407
{
408

409
    auto geom = Obj->getGeometry(GeoId);
410

411
    return isBsplinePole(geom);
412
}
413

414
bool SketcherGui::checkConstraint(const std::vector<Sketcher::Constraint*>& vals,
415
                                  ConstraintType type,
416
                                  int geoid,
417
                                  PointPos pos)
418
{
419
    for (std::vector<Sketcher::Constraint*>::const_iterator itc = vals.begin(); itc != vals.end();
420
         ++itc) {
421
        if ((*itc)->Type == type && (*itc)->First == geoid && (*itc)->FirstPos == pos) {
422
            return true;
423
        }
424
    }
425

426
    return false;
427
}
428

429
/* helper functions ======================================================*/
430

431
// Return counter-clockwise angle from horizontal out of p1 to p2 in radians.
432
double SketcherGui::GetPointAngle(const Base::Vector2d& p1, const Base::Vector2d& p2)
433
{
434
    double dX = p2.x - p1.x;
435
    double dY = p2.y - p1.y;
436
    return dY >= 0 ? atan2(dY, dX) : atan2(dY, dX) + 2 * M_PI;
437
}
438

439
// Set the two points on circles at minimal distance
440
// in concentric case set points on relative X axis
441
void SketcherGui::GetCirclesMinimalDistance(const Part::Geometry* geom1,
442
                                            const Part::Geometry* geom2,
443
                                            Base::Vector3d& point1,
444
                                            Base::Vector3d& point2)
445
{
446
    // This will throw if geom1 or geom2 are not circles or arcs
447
    auto [radius1, center1] = getRadiusCenterCircleArc(geom1);
448
    auto [radius2, center2] = getRadiusCenterCircleArc(geom2);
449

450
    point1 = center1;
451
    point2 = center2;
452

453
    Base::Vector3d v = point2 - point1;
454
    double length = v.Length();
455

456
    if (length == 0) {  // concentric case
457
        point1.x += radius1;
458
        point2.x += radius2;
459
    }
460
    else {
461
        v = v.Normalize();
462
        if (length <= std::max(radius1, radius2)) {  // inner case
463
            if (radius1 > radius2) {
464
                point1 += v * radius1;
465
                point2 += v * radius2;
466
            }
467
            else {
468
                point1 += -v * radius1;
469
                point2 += -v * radius2;
470
            }
471
        }
472
        else {  // outer case
473
            point1 += v * radius1;
474
            point2 += -v * radius2;
475
        }
476
    }
477
}
478

479
void SketcherGui::ActivateHandler(Gui::Document* doc, std::unique_ptr<DrawSketchHandler> handler)
480
{
481
    if (doc) {
482
        if (doc->getInEdit()
483
            && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
484
            SketcherGui::ViewProviderSketch* vp =
485
                static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
486
            vp->purgeHandler();
487
            vp->activateHandler(std::move(handler));
488
        }
489
    }
490
}
491

492
bool SketcherGui::isSketchInEdit(Gui::Document* doc)
493
{
494
    if (doc) {
495
        // checks if a Sketch Viewprovider is in Edit and is in no special mode
496
        auto* vp = dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
497
        return (vp != nullptr);
498
    }
499
    return false;
500
}
501

502
bool SketcherGui::isCommandActive(Gui::Document* doc, bool actsOnSelection)
503
{
504
    if (isSketchInEdit(doc)) {
505
        auto mode =
506
            static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit())->getSketchMode();
507

508
        if (mode == ViewProviderSketch::STATUS_NONE
509
            || mode == ViewProviderSketch::STATUS_SKETCH_UseHandler) {
510

511
            if (!actsOnSelection) {
512
                return true;
513
            }
514
            else if (Gui::Selection().countObjectsOfType(Sketcher::SketchObject::getClassTypeId())
515
                     > 0) {
516
                return true;
517
            }
518
        }
519
    }
520

521
    return false;
522
}
523

524
bool SketcherGui::isSketcherBSplineActive(Gui::Document* doc, bool actsOnSelection)
525
{
526
    if (doc) {
527
        // checks if a Sketch Viewprovider is in Edit and is in no special mode
528
        if (doc->getInEdit()
529
            && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
530
            if (static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit())->getSketchMode()
531
                == ViewProviderSketch::STATUS_NONE) {
532
                if (!actsOnSelection) {
533
                    return true;
534
                }
535
                else if (Gui::Selection().countObjectsOfType(
536
                             Sketcher::SketchObject::getClassTypeId())
537
                         > 0) {
538
                    return true;
539
                }
540
            }
541
        }
542
    }
543
    return false;
544
}
545

546
SketcherGui::ViewProviderSketch*
547
SketcherGui::getInactiveHandlerEditModeSketchViewProvider(Gui::Document* doc)
548
{
549
    if (doc) {
550
        return dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
551
    }
552

553
    return nullptr;
554
}
555

556
SketcherGui::ViewProviderSketch* SketcherGui::getInactiveHandlerEditModeSketchViewProvider()
557
{
558
    Gui::Document* doc = Gui::Application::Instance->activeDocument();
559

560
    return getInactiveHandlerEditModeSketchViewProvider(doc);
561
}
562

563
void SketcherGui::removeRedundantHorizontalVertical(Sketcher::SketchObject* psketch,
564
                                                    std::vector<AutoConstraint>& sug1,
565
                                                    std::vector<AutoConstraint>& sug2)
566
{
567
    if (!sug1.empty() && !sug2.empty()) {
568

569
        bool rmvhorvert = false;
570

571
        // we look for:
572
        // 1. Coincident to external on both endpoints
573
        // 2. Coincident in one endpoint to origin and pointonobject/tangent to an axis on the other
574
        auto detectredundant =
575
            [psketch](std::vector<AutoConstraint>& sug, bool& ext, bool& orig, bool& axis) {
576
                ext = false;
577
                orig = false;
578
                axis = false;
579

580
                for (std::vector<AutoConstraint>::const_iterator it = sug.begin(); it != sug.end();
581
                     ++it) {
582
                    if ((*it).Type == Sketcher::Coincident && !ext) {
583
                        const std::map<int, Sketcher::PointPos> coincidents =
584
                            psketch->getAllCoincidentPoints((*it).GeoId, (*it).PosId);
585

586
                        if (!coincidents.empty()) {
587
                            // the keys are ordered, so if the first is negative, it is coincident
588
                            // with external
589
                            ext = coincidents.begin()->first < 0;
590

591
                            std::map<int, Sketcher::PointPos>::const_iterator geoId1iterator;
592

593
                            geoId1iterator = coincidents.find(-1);
594

595
                            if (geoId1iterator != coincidents.end()) {
596
                                if ((*geoId1iterator).second == Sketcher::PointPos::start) {
597
                                    orig = true;
598
                                }
599
                            }
600
                        }
601
                        else {  // it may be that there is no constraint at all, but there is
602
                                // external geometry
603
                            ext = (*it).GeoId < 0;
604
                            orig = ((*it).GeoId == -1 && (*it).PosId == Sketcher::PointPos::start);
605
                        }
606
                    }
607
                    else if ((*it).Type == Sketcher::PointOnObject && !axis) {
608
                        axis = (((*it).GeoId == -1 && (*it).PosId == Sketcher::PointPos::none)
609
                                || ((*it).GeoId == -2 && (*it).PosId == Sketcher::PointPos::none));
610
                    }
611
                }
612
            };
613

614
        bool firstext = false, secondext = false, firstorig = false, secondorig = false,
615
             firstaxis = false, secondaxis = false;
616

617
        detectredundant(sug1, firstext, firstorig, firstaxis);
618
        detectredundant(sug2, secondext, secondorig, secondaxis);
619

620

621
        rmvhorvert =
622
            ((firstext && secondext) ||    // coincident with external on both endpoints
623
             (firstorig && secondaxis) ||  // coincident origin and point on object on other
624
             (secondorig && firstaxis));
625

626
        if (rmvhorvert) {
627
            for (std::vector<AutoConstraint>::reverse_iterator it = sug2.rbegin();
628
                 it != sug2.rend();
629
                 ++it) {
630
                if ((*it).Type == Sketcher::Horizontal || (*it).Type == Sketcher::Vertical) {
631
                    sug2.erase(std::next(it).base());
632
                    it = sug2.rbegin();  // erase invalidates the iterator
633
                }
634
            }
635
        }
636
    }
637
}
638

639
void SketcherGui::ConstraintToAttachment(Sketcher::GeoElementId element,
640
                                         Sketcher::GeoElementId attachment,
641
                                         double distance,
642
                                         App::DocumentObject* obj)
643
{
644
    if (distance == 0.) {
645

646
        if (attachment.isCurve()) {
647
            Gui::cmdAppObjectArgs(obj,
648
                                  "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
649
                                  element.GeoId,
650
                                  element.posIdAsInt(),
651
                                  attachment.GeoId);
652
        }
653
        else {
654
            Gui::cmdAppObjectArgs(obj,
655
                                  "addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ",
656
                                  element.GeoId,
657
                                  element.posIdAsInt(),
658
                                  attachment.GeoId,
659
                                  attachment.posIdAsInt());
660
        }
661
    }
662
    else {
663
        if (attachment == Sketcher::GeoElementId::VAxis) {
664
            Gui::cmdAppObjectArgs(obj,
665
                                  "addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%f)) ",
666
                                  element.GeoId,
667
                                  element.posIdAsInt(),
668
                                  distance);
669
        }
670
        else if (attachment == Sketcher::GeoElementId::HAxis) {
671
            Gui::cmdAppObjectArgs(obj,
672
                                  "addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%f)) ",
673
                                  element.GeoId,
674
                                  element.posIdAsInt(),
675
                                  distance);
676
        }
677
    }
678
}
679

680

681
// convenience functions for cursor display
682
bool SketcherGui::hideUnits()
683
{
684
    Base::Reference<ParameterGrp> hGrp = App::GetApplication()
685
                                             .GetUserParameter()
686
                                             .GetGroup("BaseApp")
687
                                             ->GetGroup("Preferences")
688
                                             ->GetGroup("Mod/Sketcher");
689
    return hGrp->GetBool("HideUnits", false);
690
}
691

692
bool SketcherGui::showCursorCoords()
693
{
694
    Base::Reference<ParameterGrp> hGrp = App::GetApplication()
695
                                             .GetUserParameter()
696
                                             .GetGroup("BaseApp")
697
                                             ->GetGroup("Preferences")
698
                                             ->GetGroup("Mod/Sketcher");
699
    return hGrp->GetBool("ShowCursorCoords", true);  // true for testing. set to false for prod.
700
}
701

702
bool SketcherGui::useSystemDecimals()
703
{
704
    Base::Reference<ParameterGrp> hGrp = App::GetApplication()
705
                                             .GetUserParameter()
706
                                             .GetGroup("BaseApp")
707
                                             ->GetGroup("Preferences")
708
                                             ->GetGroup("Mod/Sketcher");
709
    return hGrp->GetBool("UseSystemDecimals", true);
710
}
711

712
// convert value to display format %0.[digits]f. Units are displayed if
713
// preference "ShowUnits" is true, or if the unit schema in effect uses
714
// multiple units (ex. Ft/In). Digits parameter is ignored for multi-unit
715
// schemata
716
// TODO:: if the user string is delivered in 1.23e45 format, this might not work
717
//        correctly.
718
std::string SketcherGui::lengthToDisplayFormat(double value, int digits)
719
{
720
    Base::Quantity asQuantity;
721
    asQuantity.setValue(value);
722
    asQuantity.setUnit(Base::Unit::Length);
723
    QString qUserString = asQuantity.getUserString();
724
    if (Base::UnitsApi::isMultiUnitLength() || (!hideUnits() && useSystemDecimals())) {
725
        // just return the user string
726
        return Base::Tools::toStdString(qUserString);
727
    }
728

729
    // find the unit of measure
730
    double factor = 1.0;
731
    QString qUnitString;
732
    QString qtranslate = Base::UnitsApi::schemaTranslate(asQuantity, factor, qUnitString);
733
    QString unitPart = QString::fromUtf8(" ") + qUnitString;
734

735
    // get the numeric part of the user string
736
    QRegularExpression rxNoUnits(
737
        QString::fromUtf8("(.*) \\D*$"));  // text before space + any non digits at end of string
738
    QRegularExpressionMatch match = rxNoUnits.match(qUserString);
739
    if (!match.hasMatch()) {
740
        // no units in userString?
741
        return Base::Tools::toStdString(qUserString);
742
    }
743
    QString matched = match.captured(1);  // matched is the numeric part of user string
744
    int dpPos = matched.indexOf(QLocale().decimalPoint());
745
    if (dpPos < 0) {
746
        // no decimal separator (ie an integer), return all the digits
747
        if (hideUnits()) {
748
            return Base::Tools::toStdString(matched);
749
        }
750
        else {
751
            return Base::Tools::toStdString(matched + unitPart);
752
        }
753
    }
754

755
    // real number
756
    if (useSystemDecimals() && hideUnits()) {
757
        // return just the numeric part of the user string
758
        return Base::Tools::toStdString(matched);
759
    }
760

761
    // real number and not using system decimals
762
    int requiredLength = dpPos + digits + 1;
763
    if (requiredLength > matched.size()) {
764
        // just take the whole thing
765
        requiredLength = matched.size();
766
    }
767
    QString numericPart = matched.left(requiredLength);
768
    if (hideUnits()) {
769
        return Base::Tools::toStdString(numericPart);
770
    }
771
    return Base::Tools::toStdString(numericPart + unitPart);
772
}
773

774
// convert value to display format %0.[digits]f. Units are always displayed for
775
// angles - 123.456° or 12°34'56". Digits parameter is ignored for multi-unit
776
// schemata. Note small differences between this method and lengthToDisplyFormat
777
// TODO:: if the user string is delivered in 1.23e45 format, this might not work
778
//        correctly.
779
std::string SketcherGui::angleToDisplayFormat(double value, int digits)
780
{
781
    Base::Quantity asQuantity;
782
    asQuantity.setValue(value);
783
    asQuantity.setUnit(Base::Unit::Angle);
784
    QString qUserString = asQuantity.getUserString();
785
    if (Base::UnitsApi::isMultiUnitAngle()) {
786
        // just return the user string
787
        // Coin SbString doesn't handle utf8 well, so we convert to ascii
788
        QString schemeMinute = QString::fromUtf8("\xE2\x80\xB2");  // prime symbol
789
        QString schemeSecond = QString::fromUtf8("\xE2\x80\xB3");  // double prime symbol
790
        QString escapeMinute = QString::fromLatin1("\'");          // substitute ascii single quote
791
        QString escapeSecond = QString::fromLatin1("\"");          // substitute ascii double quote
792
        QString displayString = qUserString.replace(schemeMinute, escapeMinute);
793
        displayString = displayString.replace(schemeSecond, escapeSecond);
794
        return Base::Tools::toStdString(displayString);
795
    }
796

797
    // we always use use U+00B0 (°) as the unit of measure for angles in
798
    // single unit schema.  Will need a change to support rads or grads.
799
    auto qUnitString = QString::fromUtf8("°");
800
    auto decimalSep = QLocale().decimalPoint();
801

802
    // get the numeric part of the user string
803
    QRegularExpression rxNoUnits(QString::fromUtf8("(\\d*\\%1?\\d*)(\\D*)$")
804
                                     .arg(decimalSep));  // number + non digits at end of string
805
    QRegularExpressionMatch match = rxNoUnits.match(qUserString);
806
    if (!match.hasMatch()) {
807
        // no units in userString?
808
        return Base::Tools::toStdString(qUserString);
809
    }
810
    QString matched = match.captured(1);  // matched is the numeric part of user string
811
    int dpPos = matched.indexOf(decimalSep);
812
    if (dpPos < 0) {
813
        // no decimal separator (ie an integer), return all the digits
814
        return Base::Tools::toStdString(matched + qUnitString);
815
    }
816

817
    // real number
818
    if (useSystemDecimals()) {
819
        // return just the numeric part of the user string + degree symbol
820
        return Base::Tools::toStdString(matched + qUnitString);
821
    }
822

823
    // real number and not using system decimals
824
    int requiredLength = dpPos + digits + 1;
825
    if (requiredLength > matched.size()) {
826
        // just take the whole thing
827
        requiredLength = matched.size();
828
    }
829
    QString numericPart = matched.left(requiredLength);
830
    return Base::Tools::toStdString(numericPart + qUnitString);
831
}
832

833

834
bool SketcherGui::areCollinear(const Base::Vector2d& p1,
835
                               const Base::Vector2d& p2,
836
                               const Base::Vector2d& p3)
837
{
838
    Base::Vector2d u = p2 - p1;
839
    Base::Vector2d v = p3 - p2;
840
    Base::Vector2d w = p1 - p3;
841

842
    double uu = u * u;
843
    double vv = v * v;
844
    double ww = w * w;
845

846
    double eps2 = Precision::SquareConfusion();
847
    if (uu < eps2 || vv < eps2 || ww < eps2) {
848
        return true;
849
    }
850

851
    double uv = -(u * v);
852
    double vw = -(v * w);
853
    double uw = -(u * w);
854

855
    double w0 = (2 * sqrt(abs(uu * ww - uw * uw)) * uw / (uu * ww));
856
    double w1 = (2 * sqrt(abs(uu * vv - uv * uv)) * uv / (uu * vv));
857
    double w2 = (2 * sqrt(abs(vv * ww - vw * vw)) * vw / (vv * ww));
858

859
    double wx = w0 + w1 + w2;
860

861
    if (abs(wx) < Precision::Confusion()) {
862
        return true;
863
    }
864

865
    return false;
866
}
867

868
int SketcherGui::indexOfGeoId(const std::vector<int>& vec, int elem)
869
{
870
    if (elem == GeoEnum::GeoUndef) {
871
        return GeoEnum::GeoUndef;
872
    }
873
    for (size_t i = 0; i < vec.size(); i++) {
874
        if (vec[i] == elem) {
875
            return static_cast<int>(i);
876
        }
877
    }
878
    return -1;
879
}
880

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

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

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

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