1
/***************************************************************************
2
* Copyright (c) 2007 Werner Mayer <wmayer[at]users.sourceforge.net> *
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"
27
// to avoid compiler warnings of redefining contents of basic.h
28
// later by #include <Gui/ViewProvider.h>
29
# define _USE_MATH_DEFINES
33
# include <gp_Circ.hxx>
36
# include <BRepAdaptor_Curve.hxx>
37
# include <BRepAdaptor_Surface.hxx>
38
# include <Geom_Plane.hxx>
40
# include <TopoDS_Face.hxx>
41
# include <TopExp_Explorer.hxx>
44
# include <QMessageBox>
45
# include <QRegularExpression>
46
# include <QTreeWidget>
50
#include <Base/Tools.h>
51
#include <App/Application.h>
52
#include <App/Document.h>
53
#include <App/DocumentObject.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>
71
#include "ui_Mirroring.h"
74
using namespace PartGui;
77
class MirrorPlaneSelection : public Gui::SelectionFilterGate
80
explicit MirrorPlaneSelection()
81
: Gui::SelectionFilterGate()
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.
92
bool allow(App::Document* /*pDoc*/, App::DocumentObject* pObj, const char* sSubName) override
94
std::string subString(sSubName);
96
if (pObj->isDerivedFrom(Part::Plane::getClassTypeId()) || pObj->isDerivedFrom<App::Plane>()
97
|| (strstr(pObj->getNameInDocument(), "Plane") && pObj->isDerivedFrom(Part::Datum::getClassTypeId()))) {
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
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
109
if (strstr(subString.c_str(), "Edge")){
110
isEdge = true; //was edge subobject, e.g. Edge7
114
shape = Part::Feature::getShape(pObj); //no subobjects were selected, so this is entire shape of feature
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);
125
if (isFace) { //user selected a face, so use shape to get the TopoDS::Face
126
face = TopoDS::Face(shape);
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);
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
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);
145
if (isFace && face.IsNull()) { //ensure we have a good face to work with
148
if (isEdge && edge.IsNull()){ //ensure we have a good edge to work with
151
if (!isFace && !isEdge){
156
BRepAdaptor_Surface adapt(face);
157
if (adapt.GetType() != GeomAbs_Plane){
163
BRepAdaptor_Curve curve(edge);
164
if (!(curve.GetType() == GeomAbs_Circle)) {
170
} //end of if(derived from part::feature)
175
}; //end of namespace block
178
/* TRANSLATOR PartGui::Mirroring */
180
Mirroring::Mirroring(QWidget* parent)
181
: QWidget(parent), ui(new Ui_Mirroring)
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);
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()));
197
connect(ui->selectButton, &QPushButton::clicked, this, &Mirroring::onSelectButtonClicked);
199
MirrorPlaneSelection* gate = new MirrorPlaneSelection();
200
Gui::Selection().addSelectionGate(gate);
204
* Destroys the object and frees any allocated resources
206
Mirroring::~Mirroring() = default;
208
void Mirroring::onSelectButtonClicked(){
209
if (!ui->selectButton->isChecked()){
210
Gui::Selection().rmvSelectionGate();
211
ui->selectButton->setText(tr("Select reference"));
213
MirrorPlaneSelection* gate = new MirrorPlaneSelection();
214
Gui::Selection().addSelectionGate(gate);
215
ui->selectButton->setText(tr("Selecting"));
219
void Mirroring::changeEvent(QEvent *e)
221
if (e->type() == QEvent::LanguageChange) {
222
ui->retranslateUi(this);
224
QWidget::changeEvent(e);
227
void Mirroring::onSelectionChanged(const Gui::SelectionChanges &msg)
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);
241
void Mirroring::findShapes()
243
App::Document* activeDoc = App::GetApplication().getActiveDocument();
246
Gui::Document* activeGui = Gui::Application::Instance->getDocument(activeDoc);
250
this->document = QString::fromLatin1(activeDoc->getName());
251
std::vector<App::DocumentObject*> objs = activeDoc->getObjectsOfType<App::DocumentObject>();
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());
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);
270
bool Mirroring::reject()
272
Gui::Selection().rmvSelectionGate();
276
bool Mirroring::accept()
278
if (ui->shapes->selectedItems().isEmpty()) {
279
QMessageBox::critical(this, windowTitle(),
280
tr("Select a shape for mirroring, first."));
284
App::Document* activeDoc = App::GetApplication().getDocument((const char*)this->document.toLatin1());
286
QMessageBox::critical(this, windowTitle(),
287
tr("No such document '%1'.").arg(this->document));
292
unsigned int count = activeDoc->countObjectsOfType(Base::Type::fromName("Part::Mirroring"));
293
activeDoc->openTransaction("Mirroring");
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
305
} else if (index == 1){
307
} else if (index == 2){
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();
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);
324
// if we already have the suffix " (Mirror #<number>)" remove it
325
int pos = label.indexOf(rx);
327
label = label.left(pos);
328
label.append(QString::fromLatin1(" (Mirror #%1)").arg(++count));
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"
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);
350
activeDoc->commitTransaction();
351
activeDoc->recompute();
352
Gui::Selection().rmvSelectionGate();
356
// ---------------------------------------
358
TaskMirroring::TaskMirroring()
360
widget = new Mirroring();
361
addTaskBox(Gui::BitmapFactory().pixmap("Part_Mirror.svg"), widget, false);
364
bool TaskMirroring::accept()
366
return widget->accept();
369
bool TaskMirroring::reject()
371
return widget->reject();
374
#include "moc_Mirroring.cpp"