1
/***************************************************************************
2
* Copyright (c) 2016 WandererFan <wandererfan@gmail.com> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
30
# include <QMessageBox>
35
# include <BRepAdaptor_Surface.hxx>
36
# include <BRepLProp_SLProps.hxx>
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>
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>
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>
67
#include "DlgPageChooser.h"
68
#include "DrawGuiUtil.h"
69
#include "MDIViewPage.h"
71
#include "ViewProviderPage.h"
73
using namespace TechDrawGui;
74
using namespace TechDraw;
76
void DrawGuiUtil::loadArrowBox(QComboBox* qcb)
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);
87
void DrawGuiUtil::loadLineStandardsChoices(QComboBox* combo)
90
std::vector<std::string> choices = LineGenerator::getAvailableLineStandards();
91
for (auto& entry : choices) {
92
QString qentry = Base::Tools::fromStdString(entry);
93
combo->addItem(qentry);
97
void DrawGuiUtil::loadLineStyleChoices(QComboBox* combo, LineGenerator* generator)
100
std::vector<std::string> choices;
102
choices = generator->getLoadedDescriptions();
104
choices = LineGenerator::getLineDescriptions();
108
for (auto& entry : choices) {
109
QString qentry = Base::Tools::fromStdString(entry);
110
combo->addItem(qentry);
112
combo->setItemIcon(itemNumber, iconForLine(itemNumber + 1, generator));
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)
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};
127
double maxLineLength = iconSize - borderSize * 2.0;
128
QBitmap bitmap{iconSize, iconSize};
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);
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};
146
linePen.setWidthF(iconLineWeight);
147
painter.setPen(linePen);
148
double yHeight = (iconSize / 2) - (lineCount * iconLineWeight);
150
// draw multiple lines to stretch the line vertically without horizontal
152
for ( ; iLine < lineCount; iLine++){
153
painter.drawLine(borderSize, yHeight, maxLineLength, yHeight);
154
yHeight += iconLineWeight;
156
return QIcon{bitmap};
160
//===========================================================================
161
// validate helper routines
162
//===========================================================================
164
//find a page in Selection, Document or CurrentWindow.
165
TechDraw::DrawPage* DrawGuiUtil::findPage(Gui::Command* cmd, bool findAny)
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();
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
184
foundPageObjects.insert(foundPageObjects.end(), docPages.begin(), docPages.end());
186
if (foundPageObjects.empty()) {
187
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No page found"),
188
QObject::tr("No Drawing Pages available."));
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);
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()));
208
return static_cast<TechDraw::DrawPage*>(foundPageObjects.front());
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."));
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);
230
QGSPage* qp = mvp->getViewProviderPage()->getQGSPage();
231
return qp->getDrawPage();
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);
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()));
251
//only 1 page in document - use it
252
return static_cast<TechDraw::DrawPage*>(docPages.front());
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);
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()));
271
//exactly 1 page in selection, use it
272
return static_cast<TechDraw::DrawPage*>(selPages.front());
275
//we can not actually reach this point.
279
bool DrawGuiUtil::isDraftObject(App::DocumentObject* obj)
282
App::PropertyPythonObject* proxy =
283
dynamic_cast<App::PropertyPythonObject*>(obj->getPropertyByName("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;
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) {
298
else if (ss.str().find("draft") != std::string::npos) {
303
catch (Py::Exception&) {
304
Base::PyException e;// extract the Python error text
312
bool DrawGuiUtil::isArchObject(App::DocumentObject* obj)
315
App::PropertyPythonObject* proxy =
316
dynamic_cast<App::PropertyPythonObject*>(obj->getPropertyByName("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;
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) {
334
catch (Py::Exception&) {
335
Base::PyException e;// extract the Python error text
343
bool DrawGuiUtil::isArchSection(App::DocumentObject* obj)
346
App::PropertyPythonObject* proxy =
347
dynamic_cast<App::PropertyPythonObject*>(obj->getPropertyByName("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;
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) {
365
catch (Py::Exception&) {
366
Base::PyException e;// extract the Python error text
374
bool DrawGuiUtil::needPage(Gui::Command* cmd, bool 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
386
//found at least 1 page
390
//did not find any pages
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();
403
bool DrawGuiUtil::needView(Gui::Command* cmd, bool partOnly)
405
bool haveView = false;
406
if (cmd->hasActiveDocument()) {
408
auto drawPartType(TechDraw::DrawViewPart::getClassTypeId());
409
auto selParts = cmd->getDocument()->getObjectsOfType(drawPartType);
410
if (!selParts.empty()) {
415
auto drawViewType(TechDraw::DrawView::getClassTypeId());
416
auto selParts = cmd->getDocument()->getObjectsOfType(drawViewType);
417
if (!selParts.empty()) {
425
void DrawGuiUtil::dumpRectF(const char* text, const QRectF& r)
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,
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());
438
void DrawGuiUtil::dumpPointF(const char* text, const QPointF& p)
440
Base::Console().Message("DUMP - dumpPointF - %s\n", text);
441
Base::Console().Message("Point: (%.3f, %.3f)\n", p.x(), p.y());
444
std::pair<Base::Vector3d, Base::Vector3d> DrawGuiUtil::get3DDirAndRot()
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);
456
viewer = view->getViewer();
461
return std::make_pair(viewDir, viewRight);
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
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.
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
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);
487
Base::Vector3d right = viewUp.Cross(viewDir);
489
result = std::make_pair(viewDir, right);
493
std::pair<Base::Vector3d, Base::Vector3d> DrawGuiUtil::getProjDirFromFace(App::DocumentObject* obj,
494
std::string faceName)
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;
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());
510
const TopoDS_Face& face = TopoDS::Face(ts);
511
TopAbs_Orientation orient = face.Orientation();
512
BRepAdaptor_Surface adapt(face);
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;
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);
530
return std::make_pair(projDir, rotVec);
533
// converts original value to one with only digits significant figures
534
double DrawGuiUtil::roundToDigits(double original, int digits)
536
double factor = std::pow(10.0, digits);
537
double temp = original * factor;
538
double rounded = std::round(temp);
539
temp = rounded / factor;
543
// Returns true if the item or any of its descendants is selected
544
bool DrawGuiUtil::isSelectedInTree(QGraphicsItem *item)
547
if (item->isSelected()) {
551
for (QGraphicsItem *child : item->childItems()) {
552
if (isSelectedInTree(child)) {
561
// Selects or deselects the item and all its descendants
562
void DrawGuiUtil::setSelectedTree(QGraphicsItem *item, bool selected)
565
item->setSelected(selected);
567
for (QGraphicsItem *child : item->childItems()) {
568
setSelectedTree(child, selected);