FreeCAD

Форк
0
/
Mirroring.cpp 
374 строки · 14.3 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2007 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

25
#ifndef _PreComp_
26

27
// to avoid compiler warnings of redefining contents of basic.h
28
// later by #include <Gui/ViewProvider.h>
29
# define _USE_MATH_DEFINES
30
# include <cmath>
31

32
# include <gp_Ax2.hxx>
33
# include <gp_Circ.hxx>
34
# include <gp_Dir.hxx>
35
# include <gp_Pnt.hxx>
36
# include <BRepAdaptor_Curve.hxx>
37
# include <BRepAdaptor_Surface.hxx>
38
# include <Geom_Plane.hxx>
39
# include <TopoDS.hxx>
40
# include <TopoDS_Face.hxx>
41
# include <TopExp_Explorer.hxx>
42

43
# include <cfloat>
44
# include <QMessageBox>
45
# include <QRegularExpression>
46
# include <QTreeWidget>
47
# include <QComboBox>
48
#endif
49

50
#include <Base/Tools.h>
51
#include <App/Application.h>
52
#include <App/Document.h>
53
#include <App/DocumentObject.h>
54
#include <App/Link.h>
55
#include <App/Part.h>
56
#include <Gui/Application.h>
57
#include <Gui/BitmapFactory.h>
58
#include <Gui/Command.h>
59
#include <Gui/Document.h>
60
#include <Gui/Selection.h>
61
#include <Gui/Utilities.h>
62
#include <Gui/ViewProvider.h>
63
#include <Gui/WaitCursor.h>
64
#include <Mod/Part/App/PartFeature.h>
65
#include <Mod/Part/App/PrimitiveFeature.h>
66
#include <Mod/Part/App/DatumFeature.h>
67
#include <App/OriginFeature.h>
68

69
#include "Mirroring.h"
70

71
#include "ui_Mirroring.h"
72

73

74
using namespace PartGui;
75

76
namespace PartGui {
77
class MirrorPlaneSelection : public Gui::SelectionFilterGate
78
{
79
public:
80
    explicit MirrorPlaneSelection()
81
        : Gui::SelectionFilterGate()
82
    {
83
    }
84
    /**
85
     * We can't simply check if the selection is a face or an edge because only certain faces
86
     * and edges can work.  Bspline faces won't work, and only circle edges are supported.  But we
87
     * also allow document object selections for part::plane, partdesign::plane, and origin planes,
88
     * as well as any part::feature with only a single face or a single circle edge.  App::Links are
89
     * supported, provided the object they are linking to meets the above criteria.
90
     */
91

92
    bool allow(App::Document* /*pDoc*/, App::DocumentObject* pObj, const char* sSubName) override
93
    {
94
        std::string subString(sSubName);
95

96
        if (pObj->isDerivedFrom(Part::Plane::getClassTypeId()) || pObj->isDerivedFrom<App::Plane>()
97
                || (strstr(pObj->getNameInDocument(), "Plane") && pObj->isDerivedFrom(Part::Datum::getClassTypeId()))) {
98
            return true;
99
            // reference is an app::link or a part::feature or some subobject
100
        } else if (pObj->isDerivedFrom<Part::Feature>() || pObj->isDerivedFrom<App::Link>()) {
101
            bool isFace = false; //will be true if user selected face subobject or if object only has 1 face
102
            bool isEdge = false; //will be true if user selected edge subobject or if object only has 1 edge
103
            TopoDS_Shape shape;
104
            if (subString.length() > 0){
105
                shape = Part::Feature::getTopoShape(pObj, subString.c_str(), true).getShape();
106
                if (strstr(subString.c_str(), "Face")){
107
                    isFace = true; //was face subobject, e.g. Face3
108
                } else {
109
                    if (strstr(subString.c_str(), "Edge")){
110
                        isEdge = true; //was edge subobject, e.g. Edge7
111
                    }
112
                }
113
            } else {
114
                shape = Part::Feature::getShape(pObj); //no subobjects were selected, so this is entire shape of feature
115
            }
116

117
            // if there is only 1 face or 1 edge, then we don't need to force the user to select that face or edge
118
            // instead we can infer what was intended
119
            int faceCount = Part::TopoShape(shape).countSubShapes(TopAbs_FACE);
120
            int edgeCount = Part::TopoShape(shape).countSubShapes(TopAbs_EDGE);
121

122
            TopoDS_Face face;
123
            TopoDS_Edge edge;
124

125
            if (isFace) { //user selected a face, so use shape to get the TopoDS::Face
126
                face = TopoDS::Face(shape);
127
            } else {
128
                if (faceCount == 1) { //entire feature selected, but it only has 1 face, so get that face
129
                    TopoDS_Shape tdface = Part::TopoShape(shape).getSubShape(std::string("Face1").c_str());
130
                    face = TopoDS::Face(tdface);
131
                    isFace = true;
132
                }
133
            }
134
            if (!isFace && isEdge){ //don't bother with edge if we already have a face to work with
135
                edge = TopoDS::Edge(shape); //isEdge means an edge was selected
136
            } else {
137
                if (edgeCount == 1){ //we don't have a face yet and there were no edges in the subobject selection
138
                    //but since this object only has 1 edge, we use it
139
                    TopoDS_Shape tdedge = Part::TopoShape(shape).getSubShape(std::string("Edge1").c_str());
140
                    edge = TopoDS::Edge(tdedge);
141
                    isEdge = true;
142
                }
143
            }
144

145
            if (isFace && face.IsNull()) { //ensure we have a good face to work with
146
                return false;
147
            }
148
            if (isEdge && edge.IsNull()){ //ensure we have a good edge to work with
149
                return false;
150
            }
151
            if (!isFace && !isEdge){
152
                return false;
153
            }
154

155
            if (isFace) {
156
                BRepAdaptor_Surface adapt(face);
157
                if (adapt.GetType() != GeomAbs_Plane){
158
                    return false;
159
                }
160
                return true;
161
            } else {
162
                if (isEdge){
163
                    BRepAdaptor_Curve curve(edge);
164
                    if (!(curve.GetType() == GeomAbs_Circle)) {
165
                        return false;
166
                    }
167
                    return true;
168
                }
169
            }
170
        } //end of if(derived from part::feature)
171
        return true;
172
    }//end of allow()
173

174
}; //end of class
175
}; //end of namespace block
176

177

178
/* TRANSLATOR PartGui::Mirroring */
179

180
Mirroring::Mirroring(QWidget* parent)
181
  : QWidget(parent), ui(new Ui_Mirroring)
182
{
183
    ui->setupUi(this);
184
    ui->baseX->setRange(-DBL_MAX, DBL_MAX);
185
    ui->baseY->setRange(-DBL_MAX, DBL_MAX);
186
    ui->baseZ->setRange(-DBL_MAX, DBL_MAX);
187
    ui->baseX->setUnit(Base::Unit::Length);
188
    ui->baseY->setUnit(Base::Unit::Length);
189
    ui->baseZ->setUnit(Base::Unit::Length);
190
    findShapes();
191

192
    Gui::ItemViewSelection sel(ui->shapes);
193
    sel.applyFrom(Gui::Selection().getObjectsOfType(Part::Feature::getClassTypeId()));
194
    sel.applyFrom(Gui::Selection().getObjectsOfType(App::Link::getClassTypeId()));
195
    sel.applyFrom(Gui::Selection().getObjectsOfType(App::Part::getClassTypeId()));
196

197
    connect(ui->selectButton, &QPushButton::clicked, this, &Mirroring::onSelectButtonClicked);
198

199
    MirrorPlaneSelection* gate = new MirrorPlaneSelection();
200
    Gui::Selection().addSelectionGate(gate);
201
}
202

203
/*
204
 *  Destroys the object and frees any allocated resources
205
 */
206
Mirroring::~Mirroring() = default;
207

208
void Mirroring::onSelectButtonClicked(){
209
    if (!ui->selectButton->isChecked()){
210
        Gui::Selection().rmvSelectionGate();
211
        ui->selectButton->setText(tr("Select reference"));
212
    } else {
213
        MirrorPlaneSelection* gate = new MirrorPlaneSelection();
214
        Gui::Selection().addSelectionGate(gate);
215
        ui->selectButton->setText(tr("Selecting"));
216
    }
217
}
218

219
void Mirroring::changeEvent(QEvent *e)
220
{
221
    if (e->type() == QEvent::LanguageChange) {
222
        ui->retranslateUi(this);
223
    }
224
    QWidget::changeEvent(e);
225
}
226

227
void Mirroring::onSelectionChanged(const Gui::SelectionChanges &msg)
228
{
229
    if (ui->selectButton->isChecked()) {
230
        if (msg.Type == Gui::SelectionChanges::AddSelection) {
231
            std::string objName(msg.pObjectName);
232
            std::string subName(msg.pSubName);
233
            std::stringstream refStr;
234
            refStr << objName << " : [" << subName << "]";
235
            ui->referenceLineEdit->setText(QLatin1String(refStr.str().c_str()));
236
            ui->comboBox->setCurrentIndex(3);
237
        }
238
    }
239
}
240

241
void Mirroring::findShapes()
242
{
243
    App::Document* activeDoc = App::GetApplication().getActiveDocument();
244
    if (!activeDoc)
245
        return;
246
    Gui::Document* activeGui = Gui::Application::Instance->getDocument(activeDoc);
247
    if (!activeGui)
248
        return;
249

250
    this->document = QString::fromLatin1(activeDoc->getName());
251
    std::vector<App::DocumentObject*> objs = activeDoc->getObjectsOfType<App::DocumentObject>();
252

253
    for (auto obj : objs) {
254
        Part::TopoShape shape = Part::Feature::getTopoShape(obj);
255
        if (!shape.isNull()) {
256
            QString label = QString::fromUtf8(obj->Label.getValue());
257
            QString name = QString::fromLatin1(obj->getNameInDocument());
258

259
            QTreeWidgetItem* child = new QTreeWidgetItem();
260
            child->setText(0, label);
261
            child->setToolTip(0, label);
262
            child->setData(0, Qt::UserRole, name);
263
            Gui::ViewProvider* vp = activeGui->getViewProvider(obj);
264
            if (vp) child->setIcon(0, vp->getIcon());
265
            ui->shapes->addTopLevelItem(child);
266
        }
267
    }
268
}
269

270
bool Mirroring::reject()
271
{
272
    Gui::Selection().rmvSelectionGate();
273
    return true;
274
}
275

276
bool Mirroring::accept()
277
{
278
    if (ui->shapes->selectedItems().isEmpty()) {
279
        QMessageBox::critical(this, windowTitle(),
280
            tr("Select a shape for mirroring, first."));
281
        return false;
282
    }
283

284
    App::Document* activeDoc = App::GetApplication().getDocument((const char*)this->document.toLatin1());
285
    if (!activeDoc) {
286
        QMessageBox::critical(this, windowTitle(),
287
            tr("No such document '%1'.").arg(this->document));
288
        return false;
289
    }
290

291
    Gui::WaitCursor wc;
292
    unsigned int count = activeDoc->countObjectsOfType(Base::Type::fromName("Part::Mirroring"));
293
    activeDoc->openTransaction("Mirroring");
294

295
    QString shape, label, selectionString;
296
    QRegularExpression rx(QString::fromLatin1(R"( \(Mirror #\d+\)$)"));
297
    QList<QTreeWidgetItem *> items = ui->shapes->selectedItems();
298
    float normx=0, normy=0, normz=0;
299
    int index = ui->comboBox->currentIndex();
300
    std::string selection(""); //set MirrorPlane property to empty string unless
301
                                //user has selected Use selected reference in combobox
302

303
    if (index == 0){
304
        normz = 1.0f;
305
    } else if (index == 1){
306
        normy = 1.0f;
307
    } else if (index == 2){
308
        normx = 1.0f;
309
    } else if (index == 3){ //use selected reference
310
        std::vector<Gui::SelectionObject> selobjs = Gui::Selection().getSelectionEx();
311
        if (selobjs.size() == 1) {
312
            selection = selobjs[0].getAsPropertyLinkSubString();
313
        }
314
    }
315
    double basex = ui->baseX->value().getValue();
316
    double basey = ui->baseY->value().getValue();
317
    double basez = ui->baseZ->value().getValue();
318
    for (auto item : items) {
319
        shape = item->data(0, Qt::UserRole).toString();
320
        std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(item->text(0).toUtf8());
321
        label = QString::fromStdString(escapedstr);
322
        selectionString = QString::fromStdString(selection);
323

324
        // if we already have the suffix " (Mirror #<number>)" remove it
325
        int pos = label.indexOf(rx);
326
        if (pos > -1)
327
            label = label.left(pos);
328
        label.append(QString::fromLatin1(" (Mirror #%1)").arg(++count));
329

330
        QString code = QString::fromLatin1(
331
            "__doc__=FreeCAD.getDocument(\"%1\")\n"
332
            "__doc__.addObject(\"Part::Mirroring\")\n"
333
            "__doc__.ActiveObject.Source=__doc__.getObject(\"%2\")\n"
334
            "__doc__.ActiveObject.Label=u\"%3\"\n"
335
            "__doc__.ActiveObject.Normal=(%4,%5,%6)\n"
336
            "__doc__.ActiveObject.Base=(%7,%8,%9)\n"
337
            "__doc__.ActiveObject.MirrorPlane=(%10)\n"
338
            "del __doc__")
339
            .arg(this->document, shape, label)
340
            .arg(normx).arg(normy).arg(normz)
341
            .arg(basex).arg(basey).arg(basez)
342
            .arg(selectionString);
343
        Gui::Command::runCommand(Gui::Command::App, code.toLatin1());
344
        QByteArray from = shape.toLatin1();
345
        Gui::Command::copyVisual("ActiveObject", "ShapeAppearance", from);
346
        Gui::Command::copyVisual("ActiveObject", "LineColor", from);
347
        Gui::Command::copyVisual("ActiveObject", "PointColor", from);
348
    }
349

350
    activeDoc->commitTransaction();
351
    activeDoc->recompute();
352
    Gui::Selection().rmvSelectionGate();
353
    return true;
354
}
355

356
// ---------------------------------------
357

358
TaskMirroring::TaskMirroring()
359
{
360
    widget = new Mirroring();
361
    addTaskBox(Gui::BitmapFactory().pixmap("Part_Mirror.svg"), widget, false);
362
}
363

364
bool TaskMirroring::accept()
365
{
366
    return widget->accept();
367
}
368

369
bool TaskMirroring::reject()
370
{
371
    return widget->reject();
372
}
373

374
#include "moc_Mirroring.cpp"
375

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

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

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

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