FreeCAD

Форк
0
/
DrawGuiUtil.cpp 
571 строка · 20.3 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2016 WandererFan <wandererfan@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

25
#ifndef _PreComp_
26
# include <sstream>
27

28
# include <QBitmap>
29
# include <QComboBox>
30
# include <QMessageBox>
31
# include <QPointF>
32
# include <QRectF>
33
# include <QString>
34

35
# include <BRepAdaptor_Surface.hxx>
36
# include <BRepLProp_SLProps.hxx>
37
# include <gp_Dir.hxx>
38
#endif
39

40
#include <App/Application.h>
41
#include <App/Document.h>
42
#include <App/DocumentObject.h>
43
#include <App/PropertyPythonObject.h>
44
#include <Base/Console.h>
45
#include <Base/Exception.h>
46
#include <Base/Parameter.h>
47
#include <Base/Tools.h>
48
#include <Base/Type.h>
49
#include <Gui/Application.h>
50
#include <Gui/Command.h>
51
#include <Gui/Document.h>
52
#include <Gui/MainWindow.h>
53
#include <Gui/MDIView.h>
54
#include <Gui/Selection.h>
55
#include <Gui/View3DInventor.h>
56
#include <Gui/View3DInventorViewer.h>
57
#include <Gui/PrefWidgets.h>
58
#include <Inventor/SbVec3f.h>
59

60
#include <Mod/TechDraw/App/ArrowPropEnum.h>
61
#include <Mod/TechDraw/App/LineNameEnum.h>
62
#include <Mod/TechDraw/App/DrawPage.h>
63
#include <Mod/TechDraw/App/DrawUtil.h>
64
#include <Mod/TechDraw/App/DrawViewPart.h>
65
#include <Mod/TechDraw/App/LineGenerator.h>
66

67
#include "DlgPageChooser.h"
68
#include "DrawGuiUtil.h"
69
#include "MDIViewPage.h"
70
#include "QGSPage.h"
71
#include "ViewProviderPage.h"
72

73
using namespace TechDrawGui;
74
using namespace TechDraw;
75

76
void DrawGuiUtil::loadArrowBox(QComboBox* qcb)
77
{
78
    qcb->clear();
79
    int i = 0;
80
    for (; i < ArrowPropEnum::ArrowCount; i++) {
81
        qcb->addItem(QCoreApplication::translate("ArrowPropEnum", ArrowPropEnum::ArrowTypeEnums[i]));
82
        QIcon itemIcon(QString::fromUtf8(ArrowPropEnum::ArrowTypeIcons[i].c_str()));
83
        qcb->setItemIcon(i, itemIcon);
84
    }
85
}
86

87
void DrawGuiUtil::loadLineStandardsChoices(QComboBox* combo)
88
{
89
    combo->clear();
90
    std::vector<std::string> choices = LineGenerator::getAvailableLineStandards();
91
    for (auto& entry : choices) {
92
        QString qentry = Base::Tools::fromStdString(entry);
93
        combo->addItem(qentry);
94
    }
95
}
96

97
void DrawGuiUtil::loadLineStyleChoices(QComboBox* combo, LineGenerator* generator)
98
{
99
    combo->clear();
100
    std::vector<std::string> choices;
101
    if (generator) {
102
        choices = generator->getLoadedDescriptions();
103
    } else {
104
        choices = LineGenerator::getLineDescriptions();
105
    }
106

107
    int itemNumber{0};
108
    for (auto& entry : choices) {
109
        QString qentry = Base::Tools::fromStdString(entry);
110
        combo->addItem(qentry);
111
        if (generator) {
112
            combo->setItemIcon(itemNumber, iconForLine(itemNumber + 1, generator));
113
        }
114
        itemNumber++;
115
    }
116
}
117

118

119
//! make an icon that shows a sample of lineNumber in the current line standard
120
QIcon DrawGuiUtil::iconForLine(size_t lineNumber, TechDraw::LineGenerator* generator)
121
{
122
//    Base::Console().Message("DGU::iconForLine(lineNumber: %d)\n", lineNumber);
123
    constexpr int iconSize{64};
124
    constexpr int borderSize{4};
125
    constexpr double iconLineWeight{1.0};
126
    size_t lineCount{4};
127
    double maxLineLength = iconSize - borderSize * 2.0;
128
    QBitmap bitmap{iconSize, iconSize};
129
    bitmap.clear();
130

131
    QPainter painter(&bitmap);
132
    QPen linePen = generator->getLinePen(lineNumber, iconLineWeight);
133
    linePen.setDashOffset(0.0);
134
    linePen.setCapStyle(Qt::FlatCap);
135
    linePen.setColor(Qt::color1);
136

137
    // handle simple case of continuous line
138
    if (linePen.style() == Qt::SolidLine) {
139
        linePen.setWidthF(iconLineWeight * lineCount);
140
        painter.setPen(linePen);
141
        painter.drawLine(borderSize, iconSize / 2, iconSize - borderSize, iconSize / 2);
142
        return QIcon{bitmap};
143
    }
144

145
    // dashed line
146
    linePen.setWidthF(iconLineWeight);
147
    painter.setPen(linePen);
148
    double yHeight = (iconSize / 2) - (lineCount * iconLineWeight);
149
    size_t iLine = 0;
150
    // draw multiple lines to stretch the line vertically without horizontal
151
    // distortion
152
    for ( ; iLine < lineCount; iLine++){
153
        painter.drawLine(borderSize, yHeight, maxLineLength, yHeight);
154
        yHeight += iconLineWeight;
155
    }
156
    return QIcon{bitmap};
157
}
158

159

160
//===========================================================================
161
// validate helper routines
162
//===========================================================================
163

164
//find a page in Selection, Document or CurrentWindow.
165
TechDraw::DrawPage* DrawGuiUtil::findPage(Gui::Command* cmd, bool findAny)
166
{
167
    //    Base::Console().Message("DGU::findPage()\n");
168
    std::vector<std::string> names;
169
    std::vector<std::string> labels;
170
    auto docs = App::GetApplication().getDocuments();
171

172
    if (findAny) {
173
        //find a page in any open document
174
        std::vector<App::DocumentObject*> foundPageObjects;
175
        //no page found in the usual places, but we have been asked to search all
176
        //open documents for a page.
177
        auto docsAll = App::GetApplication().getDocuments();
178
        for (auto& doc : docsAll) {
179
            auto docPages = doc->getObjectsOfType(TechDraw::DrawPage::getClassTypeId());
180
            if (docPages.empty()) {
181
                //this open document has no TD pages
182
                continue;
183
            }
184
            foundPageObjects.insert(foundPageObjects.end(), docPages.begin(), docPages.end());
185
        }
186
        if (foundPageObjects.empty()) {
187
            QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No page found"),
188
                                 QObject::tr("No Drawing Pages available."));
189
            return nullptr;
190
        }
191
        else if (foundPageObjects.size() > 1) {
192
            //multiple pages available, ask for help
193
            for (auto obj : foundPageObjects) {
194
                std::string name = obj->getNameInDocument();
195
                names.push_back(name);
196
                std::string label = obj->Label.getValue();
197
                labels.push_back(label);
198
            }
199
            DlgPageChooser dlg(labels, names, Gui::getMainWindow());
200
            if (dlg.exec() == QDialog::Accepted) {
201
                std::string selName = dlg.getSelection();
202
                App::Document* doc = cmd->getDocument();
203
                return static_cast<TechDraw::DrawPage*>(doc->getObject(selName.c_str()));
204
            }
205
        }
206
        else {
207
            //only 1 page found
208
            return static_cast<TechDraw::DrawPage*>(foundPageObjects.front());
209
        }
210
    }
211

212
    //check Selection for a page
213
    std::vector<App::DocumentObject*> selPages =
214
        cmd->getSelection().getObjectsOfType(TechDraw::DrawPage::getClassTypeId());
215
    if (selPages.empty()) {
216
        //no page in selection, try this document
217
        auto docPages = cmd->getDocument()->getObjectsOfType(TechDraw::DrawPage::getClassTypeId());
218
        if (docPages.empty()) {
219
            //we are only to look in this document, and there is no page in this document
220
            QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No page found"),
221
                                 QObject::tr("No Drawing Pages in document."));
222
            return nullptr;
223
        }
224
        else if (docPages.size() > 1) {
225
            //multiple pages in document, use active page if there is one
226
            Gui::MainWindow* w = Gui::getMainWindow();
227
            Gui::MDIView* mv = w->activeWindow();
228
            MDIViewPage* mvp = dynamic_cast<MDIViewPage*>(mv);
229
            if (mvp) {
230
                QGSPage* qp = mvp->getViewProviderPage()->getQGSPage();
231
                return qp->getDrawPage();
232
            }
233
            else {
234
                // none of pages in document is active, ask for help
235
                for (auto obj : docPages) {
236
                    std::string name = obj->getNameInDocument();
237
                    names.push_back(name);
238
                    std::string label = obj->Label.getValue();
239
                    labels.push_back(label);
240
                }
241
                DlgPageChooser dlg(labels, names, Gui::getMainWindow());
242
                if (dlg.exec() == QDialog::Accepted) {
243
                    std::string selName = dlg.getSelection();
244
                    App::Document* doc = cmd->getDocument();
245
                    return static_cast<TechDraw::DrawPage*>(doc->getObject(selName.c_str()));
246
                }
247
                return nullptr;
248
            }
249
        }
250
        else {
251
            //only 1 page in document - use it
252
            return static_cast<TechDraw::DrawPage*>(docPages.front());
253
        }
254
    }
255
    else if (selPages.size() > 1) {
256
        //multiple pages in selection
257
        for (auto obj : selPages) {
258
            std::string name = obj->getNameInDocument();
259
            names.push_back(name);
260
            std::string label = obj->Label.getValue();
261
            labels.push_back(label);
262
        }
263
        DlgPageChooser dlg(labels, names, Gui::getMainWindow());
264
        if (dlg.exec() == QDialog::Accepted) {
265
            std::string selName = dlg.getSelection();
266
            App::Document* doc = cmd->getDocument();
267
            return static_cast<TechDraw::DrawPage*>(doc->getObject(selName.c_str()));
268
        }
269
    }
270
    else {
271
        //exactly 1 page in selection, use it
272
        return static_cast<TechDraw::DrawPage*>(selPages.front());
273
    }
274

275
    //we can not actually reach this point.
276
    return nullptr;
277
}
278

279
bool DrawGuiUtil::isDraftObject(App::DocumentObject* obj)
280
{
281
    bool result = false;
282
    App::PropertyPythonObject* proxy =
283
        dynamic_cast<App::PropertyPythonObject*>(obj->getPropertyByName("Proxy"));
284

285
    if (proxy) {
286
        //if no proxy, can not be Draft obj
287
        //if has proxy, might be Draft obj
288
        std::stringstream ss;
289
        Py::Object proxyObj = proxy->getValue();
290
        Base::PyGILStateLocker lock;
291
        try {
292
            if (proxyObj.hasAttr("__module__")) {
293
                Py::String mod(proxyObj.getAttr("__module__"));
294
                ss << (std::string)mod;
295
                if (ss.str().find("Draft") != std::string::npos) {
296
                    result = true;
297
                }
298
                else if (ss.str().find("draft") != std::string::npos) {
299
                    result = true;
300
                }
301
            }
302
        }
303
        catch (Py::Exception&) {
304
            Base::PyException e;// extract the Python error text
305
            e.ReportException();
306
            result = false;
307
        }
308
    }
309
    return result;
310
}
311

312
bool DrawGuiUtil::isArchObject(App::DocumentObject* obj)
313
{
314
    bool result = false;
315
    App::PropertyPythonObject* proxy =
316
        dynamic_cast<App::PropertyPythonObject*>(obj->getPropertyByName("Proxy"));
317

318
    if (proxy) {
319
        //if no proxy, can not be Arch obj
320
        //if has proxy, might be Arch obj
321
        Py::Object proxyObj = proxy->getValue();
322
        std::stringstream ss;
323
        Base::PyGILStateLocker lock;
324
        try {
325
            if (proxyObj.hasAttr("__module__")) {
326
                Py::String mod(proxyObj.getAttr("__module__"));
327
                ss << (std::string)mod;
328
                //does this have to be an ArchSection, or can it be any Arch object?
329
                if (ss.str().find("Arch") != std::string::npos) {
330
                    result = true;
331
                }
332
            }
333
        }
334
        catch (Py::Exception&) {
335
            Base::PyException e;// extract the Python error text
336
            e.ReportException();
337
            result = false;
338
        }
339
    }
340
    return result;
341
}
342

343
bool DrawGuiUtil::isArchSection(App::DocumentObject* obj)
344
{
345
    bool result = false;
346
    App::PropertyPythonObject* proxy =
347
        dynamic_cast<App::PropertyPythonObject*>(obj->getPropertyByName("Proxy"));
348

349
    if (proxy) {
350
        //if no proxy, can not be Arch obj
351
        //if has proxy, might be Arch obj
352
        Py::Object proxyObj = proxy->getValue();
353
        std::stringstream ss;
354
        Base::PyGILStateLocker lock;
355
        try {
356
            if (proxyObj.hasAttr("__module__")) {
357
                Py::String mod(proxyObj.getAttr("__module__"));
358
                ss << (std::string)mod;
359
                //does this have to be an ArchSection, or can it be other Arch objects?
360
                if (ss.str().find("ArchSectionPlane") != std::string::npos) {
361
                    result = true;
362
                }
363
            }
364
        }
365
        catch (Py::Exception&) {
366
            Base::PyException e;// extract the Python error text
367
            e.ReportException();
368
            result = false;
369
        }
370
    }
371
    return result;
372
}
373

374
bool DrawGuiUtil::needPage(Gui::Command* cmd, bool findAny)
375
{
376
    if (findAny) {
377
        //look for any page in any open document
378
        auto docsAll = App::GetApplication().getDocuments();
379
        for (auto& doc : docsAll) {
380
            auto docPages = doc->getObjectsOfType(TechDraw::DrawPage::getClassTypeId());
381
            if (docPages.empty()) {
382
                //this open document has no TD pages
383
                continue;
384
            }
385
            else {
386
                //found at least 1 page
387
                return true;
388
            }
389
        }
390
        //did not find any pages
391
        return false;
392
    }
393

394
    //need a Document and a Page
395
    if (cmd->hasActiveDocument()) {
396
        auto drawPageType(TechDraw::DrawPage::getClassTypeId());
397
        auto selPages = cmd->getDocument()->getObjectsOfType(drawPageType);
398
        return !selPages.empty();
399
    }
400
    return false;
401
}
402

403
bool DrawGuiUtil::needView(Gui::Command* cmd, bool partOnly)
404
{
405
    bool haveView = false;
406
    if (cmd->hasActiveDocument()) {
407
        if (partOnly) {
408
            auto drawPartType(TechDraw::DrawViewPart::getClassTypeId());
409
            auto selParts = cmd->getDocument()->getObjectsOfType(drawPartType);
410
            if (!selParts.empty()) {
411
                haveView = true;
412
            }
413
        }
414
        else {
415
            auto drawViewType(TechDraw::DrawView::getClassTypeId());
416
            auto selParts = cmd->getDocument()->getObjectsOfType(drawViewType);
417
            if (!selParts.empty()) {
418
                haveView = true;
419
            }
420
        }
421
    }
422
    return haveView;
423
}
424

425
void DrawGuiUtil::dumpRectF(const char* text, const QRectF& r)
426
{
427
    Base::Console().Message("DUMP - dumpRectF - %s\n", text);
428
    double left = r.left();
429
    double right = r.right();
430
    double top = r.top();
431
    double bottom = r.bottom();
432
    Base::Console().Message("Extents: L: %.3f, R: %.3f, T: %.3f, B: %.3f\n", left, right, top,
433
                            bottom);
434
    Base::Console().Message("Size: W: %.3f H: %.3f\n", r.width(), r.height());
435
    Base::Console().Message("Centre: (%.3f, %.3f)\n", r.center().x(), r.center().y());
436
}
437

438
void DrawGuiUtil::dumpPointF(const char* text, const QPointF& p)
439
{
440
    Base::Console().Message("DUMP - dumpPointF - %s\n", text);
441
    Base::Console().Message("Point: (%.3f, %.3f)\n", p.x(), p.y());
442
}
443

444
std::pair<Base::Vector3d, Base::Vector3d> DrawGuiUtil::get3DDirAndRot()
445
{
446
    std::pair<Base::Vector3d, Base::Vector3d> result;
447
    Base::Vector3d viewDir(0.0, -1.0, 0.0); //default to front
448
    Base::Vector3d viewUp(0.0, 0.0, 1.0);   //default to top
449
    Base::Vector3d viewRight(1.0, 0.0, 0.0);//default to right
450
    std::list<Gui::MDIView*> mdis = Gui::Application::Instance->activeDocument()->getMDIViews();
451
    Gui::View3DInventor* view;
452
    Gui::View3DInventorViewer* viewer = nullptr;
453
    for (auto& m : mdis) {//find the 3D viewer
454
        view = dynamic_cast<Gui::View3DInventor*>(m);
455
        if (view) {
456
            viewer = view->getViewer();
457
            break;
458
        }
459
    }
460
    if (!viewer) {
461
        return std::make_pair(viewDir, viewRight);
462
    }
463

464
    // Coin is giving us a values like 0.000000134439 instead of 0.000000000000.
465
    // This small difference caused circles to be projected as ellipses among other
466
    // problems.
467
    // Since SbVec3f is single precision floating point, it is only good to 6-9
468
    // significant decimal digits, and the rest of TechDraw works with doubles
469
    // that are good to 15-18 significant decimal digits.
470
    // But. When a float is promoted to double the value is supposed to be unchanged!
471
    // So where do the garbage digits come from???
472
    // In any case, if we restrict directions to 6 digits, we avoid the problem.
473
    int digits(6);
474
    SbVec3f dvec = viewer->getViewDirection();
475
    double dvecX = roundToDigits(dvec[0], digits);
476
    double dvecY = roundToDigits(dvec[1], digits);
477
    double dvecZ = roundToDigits(dvec[2], digits);
478
    viewDir = Base::Vector3d(dvecX, dvecY, dvecZ);
479
    viewDir = viewDir * (-1.0);// Inventor dir is opposite TD projection dir
480

481
    SbVec3f upvec = viewer->getUpDirection();
482
    double upvecX = roundToDigits(upvec[0], digits);
483
    double upvecY = roundToDigits(upvec[1], digits);
484
    double upvecZ = roundToDigits(upvec[2], digits);
485
    viewUp = Base::Vector3d(upvecX, upvecY, upvecZ);
486

487
    Base::Vector3d right = viewUp.Cross(viewDir);
488

489
    result = std::make_pair(viewDir, right);
490
    return result;
491
}
492

493
std::pair<Base::Vector3d, Base::Vector3d> DrawGuiUtil::getProjDirFromFace(App::DocumentObject* obj,
494
                                                                          std::string faceName)
495
{
496
    std::pair<Base::Vector3d, Base::Vector3d> d3Dirs = get3DDirAndRot();
497
    std::pair<Base::Vector3d, Base::Vector3d> dirs;
498
    dirs.first = Base::Vector3d(0.0, 0.0, 1.0);//set a default
499
    dirs.second = Base::Vector3d(1.0, 0.0, 0.0);
500
    Base::Vector3d projDir, rotVec;
501
    projDir = d3Dirs.first;
502
    rotVec = d3Dirs.second;
503

504
    auto ts = Part::Feature::getShape(obj, faceName.c_str(), true);
505
    if (ts.IsNull() || ts.ShapeType() != TopAbs_FACE) {
506
        Base::Console().Warning("getProjDirFromFace(%s) is not a Face\n", faceName.c_str());
507
        return dirs;
508
    }
509

510
    const TopoDS_Face& face = TopoDS::Face(ts);
511
    TopAbs_Orientation orient = face.Orientation();
512
    BRepAdaptor_Surface adapt(face);
513

514
    double u1 = adapt.FirstUParameter();
515
    double u2 = adapt.LastUParameter();
516
    double v1 = adapt.FirstVParameter();
517
    double v2 = adapt.LastVParameter();
518
    double uMid = (u1 + u2) / 2.0;
519
    double vMid = (v1 + v2) / 2.0;
520

521
    BRepLProp_SLProps props(adapt, uMid, vMid, 2, Precision::Confusion());
522
    if (props.IsNormalDefined()) {
523
        gp_Dir vec = props.Normal();
524
        projDir = Base::Vector3d(vec.X(), vec.Y(), vec.Z());
525
        if (orient != TopAbs_FORWARD) {
526
            projDir = projDir * (-1.0);
527
        }
528
    }
529

530
    return std::make_pair(projDir, rotVec);
531
}
532

533
// converts original value to one with only digits significant figures
534
double DrawGuiUtil::roundToDigits(double original, int digits)
535
{
536
    double factor = std::pow(10.0, digits);
537
    double temp = original * factor;
538
    double rounded = std::round(temp);
539
    temp = rounded / factor;
540
    return temp;
541
}
542

543
// Returns true if the item or any of its descendants is selected
544
bool DrawGuiUtil::isSelectedInTree(QGraphicsItem *item)
545
{
546
    if (item) {
547
        if (item->isSelected()) {
548
            return true;
549
        }
550

551
        for (QGraphicsItem *child : item->childItems()) {
552
           if (isSelectedInTree(child)) {
553
               return true;
554
           }
555
        }
556
    }
557

558
    return false;
559
}
560

561
// Selects or deselects the item and all its descendants
562
void DrawGuiUtil::setSelectedTree(QGraphicsItem *item, bool selected)
563
{
564
    if (item) {
565
        item->setSelected(selected);
566

567
        for (QGraphicsItem *child : item->childItems()) {
568
            setSelectedTree(child, selected);
569
        }
570
    }
571
}
572

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

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

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

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