FreeCAD

Форк
0
/
DlgRevolution.cpp 
584 строки · 19.7 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2009 Werner Mayer <wmayer[at]users.sourceforge.net>     *
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 <QMessageBox>
26
# include <BRep_Tool.hxx>
27
# include <BRepAdaptor_Curve.hxx>
28
# include <Precision.hxx>
29
# include <ShapeExtend_Explorer.hxx>
30
# include <TopExp_Explorer.hxx>
31
# include <TopoDS.hxx>
32
# include <TopoDS_Edge.hxx>
33
# include <TopTools_HSequenceOfShape.hxx>
34
#endif
35

36
#include <App/Application.h>
37
#include <App/Document.h>
38
#include <App/DocumentObject.h>
39
#include <App/Link.h>
40
#include <App/Part.h>
41
#include <Gui/Application.h>
42
#include <Gui/BitmapFactory.h>
43
#include <Gui/Command.h>
44
#include <Gui/Document.h>
45
#include <Gui/Utilities.h>
46
#include <Gui/ViewProvider.h>
47
#include <Gui/WaitCursor.h>
48
#include <Mod/Part/App/FeatureRevolution.h>
49

50
#include "DlgRevolution.h"
51
#include "ui_DlgRevolution.h"
52

53

54
using namespace PartGui;
55

56
class DlgRevolution::EdgeSelection : public Gui::SelectionFilterGate
57
{
58
public:
59
    bool canSelect;
60

61
    EdgeSelection()
62
        : Gui::SelectionFilterGate(nullPointer())
63
    {
64
        canSelect = false;
65
    }
66
    bool allow(App::Document* /*pDoc*/, App::DocumentObject*pObj, const char*sSubName) override
67
    {
68
        this->canSelect = false;
69

70
        if (!sSubName || sSubName[0] == '\0')
71
            return false;
72
        std::string element(sSubName);
73
        if (element.substr(0,4) != "Edge")
74
            return false;
75
        Part::TopoShape part = Part::Feature::getTopoShape(pObj);
76
        if (part.isNull()) {
77
            return false;
78
        }
79
        try {
80
            TopoDS_Shape sub = part.getSubShape(sSubName);
81
            if (!sub.IsNull() && sub.ShapeType() == TopAbs_EDGE) {
82
                const TopoDS_Edge& edge = TopoDS::Edge(sub);
83
                BRepAdaptor_Curve adapt(edge);
84
                if (adapt.GetType() == GeomAbs_Line || adapt.GetType() == GeomAbs_Circle) {
85
                    this->canSelect = true;
86
                    return true;
87
                }
88
            }
89
        }
90
        catch (...) {
91
        }
92

93
        return false;
94
    }
95
};
96

97
DlgRevolution::DlgRevolution(QWidget* parent, Qt::WindowFlags fl)
98
  : QDialog(parent, fl)
99
  , ui(new Ui_DlgRevolution)
100
  , filter(nullptr)
101
{
102
    ui->setupUi(this);
103
    setupConnections();
104

105
    ui->xPos->setRange(-DBL_MAX,DBL_MAX);
106
    ui->yPos->setRange(-DBL_MAX,DBL_MAX);
107
    ui->zPos->setRange(-DBL_MAX,DBL_MAX);
108
    ui->xPos->setUnit(Base::Unit::Length);
109
    ui->yPos->setUnit(Base::Unit::Length);
110
    ui->zPos->setUnit(Base::Unit::Length);
111

112
    ui->xDir->setRange(-DBL_MAX,DBL_MAX);
113
    ui->yDir->setRange(-DBL_MAX,DBL_MAX);
114
    ui->zDir->setRange(-DBL_MAX,DBL_MAX);
115
    ui->xDir->setUnit(Base::Unit());
116
    ui->yDir->setUnit(Base::Unit());
117
    ui->zDir->setUnit(Base::Unit());
118
    ui->zDir->setValue(1.0);
119

120
    ui->angle->setUnit(Base::Unit::Angle);
121
    ui->angle->setValue(360.0);
122
    findShapes();
123

124
    Gui::ItemViewSelection sel(ui->treeWidget);
125
    sel.applyFrom(Gui::Selection().getObjectsOfType(Part::Feature::getClassTypeId()));
126
    sel.applyFrom(Gui::Selection().getObjectsOfType(App::Link::getClassTypeId()));
127
    sel.applyFrom(Gui::Selection().getObjectsOfType(App::Part::getClassTypeId()));
128

129
    connect(ui->txtAxisLink, &QLineEdit::textChanged, this, &DlgRevolution::onAxisLinkTextChanged);
130

131
    autoSolid();
132
}
133

134
/*
135
 *  Destroys the object and frees any allocated resources
136
 */
137
DlgRevolution::~DlgRevolution()
138
{
139
    // no need to delete child widgets, Qt does it all for us
140
    Gui::Selection().rmvSelectionGate();
141
}
142

143
void DlgRevolution::setupConnections()
144
{
145
    // clang-format off
146
    connect(ui->selectLine, &QPushButton::clicked,
147
            this, &DlgRevolution::onSelectLineClicked);
148
    connect(ui->btnX, &QPushButton::clicked,
149
            this, &DlgRevolution::onButtonXClicked);
150
    connect(ui->btnY, &QPushButton::clicked,
151
            this, &DlgRevolution::onButtonYClicked);
152
    connect(ui->btnZ, &QPushButton::clicked,
153
            this, &DlgRevolution::onButtonZClicked);
154
    connect(ui->txtAxisLink, &QLineEdit::textChanged,
155
            this, &DlgRevolution::onAxisLinkTextChanged);
156
    // clang-format on
157
}
158

159
Base::Vector3d DlgRevolution::getDirection() const
160
{
161
    return Base::Vector3d(
162
                ui->xDir->value().getValue(),
163
                ui->yDir->value().getValue(),
164
                ui->zDir->value().getValue());
165
}
166

167
Base::Vector3d DlgRevolution::getPosition() const
168
{
169
    return Base::Vector3d(
170
                ui->xPos->value().getValueAs(Base::Quantity::MilliMetre),
171
                ui->yPos->value().getValueAs(Base::Quantity::MilliMetre),
172
                ui->zPos->value().getValueAs(Base::Quantity::MilliMetre));
173
}
174

175
void DlgRevolution::getAxisLink(App::PropertyLinkSub &lnk) const
176
{
177
    QString text = ui->txtAxisLink->text();
178

179
    if (text.length() == 0) {
180
        lnk.setValue(nullptr);
181
    } else {
182
        QStringList parts = text.split(QChar::fromLatin1(':'));
183
        App::DocumentObject* obj = App::GetApplication().getActiveDocument()->getObject(parts[0].toLatin1());
184
        if(!obj){
185
            throw Base::ValueError(tr("Object not found: %1").arg(parts[0]).toUtf8().constData());
186
        }
187
        lnk.setValue(obj);
188
        if (parts.size() == 1) {
189
            return;
190
        } else if (parts.size() == 2) {
191
            std::vector<std::string> subs;
192
            subs.emplace_back(parts[1].toLatin1().constData());
193
            lnk.setValue(obj,subs);
194
        }
195
    }
196

197
}
198

199
double DlgRevolution::getAngle() const
200
{
201
    return ui->angle->value().getValueAs(Base::Quantity::Degree);
202
}
203

204
void DlgRevolution::setDirection(Base::Vector3d dir)
205
{
206
    ui->xDir->setValue(dir.x);
207
    ui->yDir->setValue(dir.y);
208
    ui->zDir->setValue(dir.z);
209
}
210

211
void DlgRevolution::setPosition(Base::Vector3d pos)
212
{
213
    ui->xPos->setValue(pos.x);
214
    ui->yPos->setValue(pos.y);
215
    ui->zPos->setValue(pos.z);
216
}
217

218
void DlgRevolution::setAxisLink(const App::PropertyLinkSub& lnk)
219
{
220
    if (!lnk.getValue()){
221
        ui->txtAxisLink->clear();
222
        return;
223
    }
224
    if (lnk.getSubValues().size() == 1){
225
        this->setAxisLink(lnk.getValue()->getNameInDocument(), lnk.getSubValues()[0].c_str());
226
    } else {
227
        this->setAxisLink(lnk.getValue()->getNameInDocument(), "");
228
    }
229
}
230

231
void DlgRevolution::setAxisLink(const char* objname, const char* subname)
232
{
233
    if(objname && strlen(objname) > 0){
234
        QString txt = QString::fromLatin1(objname);
235
        if (subname && strlen(subname) > 0){
236
            txt = txt + QString::fromLatin1(":") + QString::fromLatin1(subname);
237
        }
238
        ui->txtAxisLink->setText(txt);
239
    } else {
240
        ui->txtAxisLink->clear();
241
    }
242
}
243

244
std::vector<App::DocumentObject*> DlgRevolution::getShapesToRevolve() const
245
{
246
    QList<QTreeWidgetItem *> items = ui->treeWidget->selectedItems();
247
    App::Document* doc = App::GetApplication().getActiveDocument();
248
    if (!doc)
249
        throw Base::RuntimeError("Document lost");
250

251
    std::vector<App::DocumentObject*> objects;
252
    for (auto item : items) {
253
        App::DocumentObject* obj = doc->getObject(item->data(0, Qt::UserRole).toString().toLatin1());
254
        if (!obj)
255
            throw Base::RuntimeError("Object not found");
256
        objects.push_back(obj);
257
    }
258
    return objects;
259
}
260

261
bool DlgRevolution::validate()
262
{
263
    //check source shapes
264
    if (ui->treeWidget->selectedItems().isEmpty()) {
265
        QMessageBox::critical(this, windowTitle(),
266
            tr("Select a shape for revolution, first."));
267
        return false;
268
    }
269

270
    //check axis link
271
    bool axisLinkIsValid = false;
272
    bool axisLinkHasAngle = false;
273
    try{
274
        App::PropertyLinkSub lnk;
275
        this->getAxisLink(lnk);
276
        double angle_edge = 1e100;
277
        Base::Vector3d axis, center;
278
        axisLinkIsValid = Part::Revolution::fetchAxisLink(lnk, center, axis, angle_edge);
279
        axisLinkHasAngle = angle_edge != 1e100;
280
    } catch(Base::Exception &err) {
281
        QMessageBox::critical(this, windowTitle(),
282
            tr("Revolution axis link is invalid.\n\n%1").arg(QCoreApplication::translate("Exception", err.what())));
283
        ui->txtAxisLink->setFocus();
284
        return false;
285
    } catch(Standard_Failure &err) {
286
        QMessageBox::critical(this, windowTitle(),
287
            tr("Revolution axis link is invalid.\n\n%1").arg(QString::fromLocal8Bit(err.GetMessageString())));
288
        ui->txtAxisLink->setFocus();
289
        return false;
290
    } catch(...) {
291
        QMessageBox::critical(this, windowTitle(),
292
            tr("Revolution axis link is invalid.\n\n%1").arg(tr("Unknown error")));
293
        ui->txtAxisLink->setFocus();
294
        return false;
295
    }
296

297
    //check axis dir
298
    if (!axisLinkIsValid){
299
        if(this->getDirection().Length() < Precision::Confusion()){
300
            QMessageBox::critical(this, windowTitle(),
301
                tr("Revolution axis direction is zero-length. It must be non-zero."));
302
            ui->xDir->setFocus();
303
            return false;
304
        }
305
    }
306

307
    //check angle
308
    if (!axisLinkHasAngle){
309
        if (fabs(this->getAngle() / 180.0 * M_PI) < Precision::Angular()) {
310
            QMessageBox::critical(this, windowTitle(),
311
                tr("Revolution angle span is zero. It must be non-zero."));
312
            ui->angle->setFocus();
313
            return false;
314
        }
315
    }
316

317
    return true;
318
}
319

320
void DlgRevolution::changeEvent(QEvent *e)
321
{
322
    if (e->type() == QEvent::LanguageChange) {
323
        ui->retranslateUi(this);
324
    }
325
    else {
326
        QDialog::changeEvent(e);
327
    }
328
}
329

330
void DlgRevolution::keyPressEvent(QKeyEvent* ke)
331
{
332
    // The revolution dialog is embedded into a task panel
333
    // which is a parent widget and will handle the event
334
    ke->ignore();
335
}
336

337
void DlgRevolution::findShapes()
338
{
339
    App::Document* activeDoc = App::GetApplication().getActiveDocument();
340
    if (!activeDoc)
341
        return;
342
    Gui::Document* activeGui = Gui::Application::Instance->getDocument(activeDoc);
343

344
    std::vector<App::DocumentObject*> objs = activeDoc->getObjectsOfType<App::DocumentObject>();
345

346
    for (auto obj : objs) {
347
        Part::TopoShape topoShape = Part::Feature::getTopoShape(obj);
348
        if (topoShape.isNull()) {
349
            continue;
350
        }
351
        TopoDS_Shape shape = topoShape.getShape();
352
        if (shape.IsNull()) continue;
353

354
        TopExp_Explorer xp;
355
        xp.Init(shape,TopAbs_SOLID);
356
        if (xp.More()) continue; // solids not allowed
357
        xp.Init(shape,TopAbs_COMPSOLID);
358
        if (xp.More()) continue; // compound solids not allowed
359
        // So allowed are: vertex, edge, wire, face, shell and compound
360
        QTreeWidgetItem* item = new QTreeWidgetItem(ui->treeWidget);
361
        item->setText(0, QString::fromUtf8(obj->Label.getValue()));
362
        item->setData(0, Qt::UserRole, QString::fromLatin1(obj->getNameInDocument()));
363
        Gui::ViewProvider* vp = activeGui->getViewProvider(obj);
364
        if (vp) item->setIcon(0, vp->getIcon());
365
    }
366
}
367

368
void DlgRevolution::accept()
369
{
370
    if (!this->validate())
371
        return;
372
    Gui::WaitCursor wc;
373
    App::Document* activeDoc = App::GetApplication().getActiveDocument();
374
    activeDoc->openTransaction("Revolve");
375

376
    try{
377
        QString shape, type, name, solid;
378
        QList<QTreeWidgetItem *> items = ui->treeWidget->selectedItems();
379
        if (ui->checkSolid->isChecked()) {
380
            solid = QString::fromLatin1("True");}
381
        else {
382
            solid = QString::fromLatin1("False");}
383

384
        App::PropertyLinkSub axisLink;
385
        this->getAxisLink(axisLink);
386
        QString strAxisLink;
387
        if (axisLink.getValue()){
388
            strAxisLink = QString::fromLatin1("(App.ActiveDocument.%1, %2)")
389
                    .arg(QString::fromLatin1(axisLink.getValue()->getNameInDocument()),
390
                         axisLink.getSubValues().size() ==  1 ?
391
                             QString::fromLatin1("\"%1\"").arg(QString::fromLatin1(axisLink.getSubValues()[0].c_str()))
392
                             : QString() );
393
        } else {
394
            strAxisLink = QString::fromLatin1("None");
395
        }
396

397
        QString symmetric;
398
        if (ui->checkSymmetric->isChecked()) {
399
            symmetric = QString::fromLatin1("True");}
400
        else {
401
            symmetric = QString::fromLatin1("False");}
402

403
        for (auto item : items) {
404
            shape = item->data(0, Qt::UserRole).toString();
405
            type = QString::fromLatin1("Part::Revolution");
406
            name = QString::fromLatin1(activeDoc->getUniqueObjectName("Revolve").c_str());
407
            Base::Vector3d axis = this->getDirection();
408
            Base::Vector3d pos = this->getPosition();
409

410

411
            QString code = QString::fromLatin1(
412
                "FreeCAD.ActiveDocument.addObject(\"%1\",\"%2\")\n"
413
                "FreeCAD.ActiveDocument.%2.Source = FreeCAD.ActiveDocument.%3\n"
414
                "FreeCAD.ActiveDocument.%2.Axis = (%4,%5,%6)\n"
415
                "FreeCAD.ActiveDocument.%2.Base = (%7,%8,%9)\n"
416
                "FreeCAD.ActiveDocument.%2.Angle = %10\n"
417
                "FreeCAD.ActiveDocument.%2.Solid = %11\n"
418
                "FreeCAD.ActiveDocument.%2.AxisLink = %12\n"
419
                "FreeCAD.ActiveDocument.%2.Symmetric = %13\n"
420
                "FreeCADGui.ActiveDocument.%3.Visibility = False\n")
421
                .arg(type, name, shape) //%1, 2, 3
422
                .arg(axis.x,0,'f',15) //%4
423
                .arg(axis.y,0,'f',15) //%5
424
                .arg(axis.z,0,'f',15) //%6
425
                .arg(pos.x, 0,'f',15) //%7
426
                .arg(pos.y, 0,'f',15) //%8
427
                .arg(pos.z, 0,'f',15) //%9
428
                .arg(getAngle(),0,'f',15) //%10
429
                .arg(solid, //%11
430
                     strAxisLink, //%12
431
                     symmetric) //%13
432
                ;
433
            Gui::Command::runCommand(Gui::Command::App, code.toLatin1());
434
            QByteArray to = name.toLatin1();
435
            QByteArray from = shape.toLatin1();
436
            Gui::Command::copyVisual(to, "ShapeAppearance", from);
437
            Gui::Command::copyVisual(to, "LineColor", from);
438
            Gui::Command::copyVisual(to, "PointColor", from);
439
        }
440

441
        activeDoc->commitTransaction();
442
        activeDoc->recompute();
443
    } catch (Base::Exception &err) {
444
        QMessageBox::critical(this, windowTitle(),
445
            tr("Creating Revolve failed.\n\n%1").arg(QCoreApplication::translate("Exception", err.what())));
446
        return;
447
    } catch (...){
448
        QMessageBox::critical(this, windowTitle(),
449
            tr("Creating Revolve failed.\n\n%1").arg(QString::fromUtf8("Unknown error")));
450
        return;
451
    }
452

453
    QDialog::accept();
454
}
455

456
void DlgRevolution::onSelectLineClicked()
457
{
458
    if (!filter) {
459
        filter = new EdgeSelection();
460
        Gui::Selection().addSelectionGate(filter);
461
        ui->selectLine->setText(tr("Selecting... (line or arc)"));
462
    } else {
463
        Gui::Selection().rmvSelectionGate();
464
        filter = nullptr;
465
        ui->selectLine->setText(tr("Select reference"));
466
    }
467
}
468

469
void DlgRevolution::onButtonXClicked()
470
{
471
    setDirection(Base::Vector3d(1,0,0));
472
    if (!ui->xDir->isEnabled())
473
        ui->txtAxisLink->clear();
474
}
475

476
void DlgRevolution::onButtonYClicked()
477
{
478
    setDirection(Base::Vector3d(0,1,0));
479
    if (!ui->xDir->isEnabled())
480
        ui->txtAxisLink->clear();
481
}
482

483
void DlgRevolution::onButtonZClicked()
484
{
485
    setDirection(Base::Vector3d(0,0,1));
486
    if (!ui->xDir->isEnabled())
487
        ui->txtAxisLink->clear();
488
}
489

490
void DlgRevolution::onAxisLinkTextChanged(QString)
491
{
492
    bool en = true;
493
    try{
494
        Base::Vector3d pos, dir;
495
        double angle_edge = 1e100;
496
        App::PropertyLinkSub lnk; this->getAxisLink(lnk);
497
        bool fetched = Part::Revolution::fetchAxisLink(lnk, pos, dir, angle_edge);
498
        if (fetched){
499
            this->setDirection(dir);
500
            this->setPosition(pos);
501
            if (angle_edge != 1e100){
502
                ui->angle->setValue(0.0);
503
            } else if (fabs(ui->angle->value().getValue()) < 1e-12) {
504
                ui->angle->setValue(360.0);
505
            }
506
            en = false;
507
        }
508
    } catch (Base::Exception &){
509

510
    } catch (...){
511

512
    }
513
    ui->xDir->setEnabled(en);
514
    ui->yDir->setEnabled(en);
515
    ui->zDir->setEnabled(en);
516
    ui->xPos->setEnabled(en);
517
    ui->yPos->setEnabled(en);
518
    ui->zPos->setEnabled(en);
519
}
520

521
void DlgRevolution::onSelectionChanged(const Gui::SelectionChanges& msg)
522
{
523
    if (msg.Type == Gui::SelectionChanges::AddSelection) {
524
        if (filter && filter->canSelect) {
525
            this->setAxisLink(msg.pObjectName, msg.pSubName);
526
        }
527
    }
528
}
529

530
App::DocumentObject&DlgRevolution::getShapeToRevolve() const
531
{
532
    std::vector<App::DocumentObject*> objs = this->getShapesToRevolve();
533
    if (objs.empty())
534
        throw Base::ValueError("No shapes selected");
535
    return *(objs[0]);
536
}
537

538
void DlgRevolution::autoSolid()
539
{
540
    try{
541
        App::DocumentObject &dobj = this->getShapeToRevolve();
542
        Part::TopoShape topoShape = Part::Feature::getTopoShape(&dobj);
543
        if (topoShape.isNull()) {
544
            return;
545
        } else {
546
            TopoDS_Shape sh = topoShape.getShape();
547
            if (sh.IsNull())
548
                return;
549
            ShapeExtend_Explorer xp;
550
            Handle(TopTools_HSequenceOfShape) leaves = xp.SeqFromCompound(sh, /*recursive= */Standard_True);
551
            int cntClosedWires = 0;
552
            for (int i = 0; i < leaves->Length(); i++) {
553
                const TopoDS_Shape &leaf = leaves->Value(i+1);
554
                if (leaf.IsNull())
555
                    return;
556
                if (leaf.ShapeType() == TopAbs_WIRE || leaf.ShapeType() == TopAbs_EDGE){
557
                    if (BRep_Tool::IsClosed(leaf)){
558
                        cntClosedWires++;
559
                    }
560
                }
561
            }
562
            ui->checkSolid->setChecked( cntClosedWires == leaves->Length() );
563
        }
564
    } catch(...){
565

566
    }
567

568
}
569

570
// ---------------------------------------
571

572
TaskRevolution::TaskRevolution()
573
{
574
    widget = new DlgRevolution();
575
    addTaskBox(Gui::BitmapFactory().pixmap("Part_Revolve"), widget);
576
}
577

578
bool TaskRevolution::accept()
579
{
580
    widget->accept();
581
    return (widget->result() == QDialog::Accepted);
582
}
583

584
#include "moc_DlgRevolution.cpp"
585

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

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

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

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