/*************************************************************************** * Copyright (c) 2007 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Mirroring.h" #include "ui_Mirroring.h" using namespace PartGui; namespace PartGui { class MirrorPlaneSelection : public Gui::SelectionFilterGate { public: explicit MirrorPlaneSelection() : Gui::SelectionFilterGate() { } /** * We can't simply check if the selection is a face or an edge because only certain faces * and edges can work. Bspline faces won't work, and only circle edges are supported. But we * also allow document object selections for part::plane, partdesign::plane, and origin planes, * as well as any part::feature with only a single face or a single circle edge. App::Links are * supported, provided the object they are linking to meets the above criteria. */ bool allow(App::Document* /*pDoc*/, App::DocumentObject* pObj, const char* sSubName) override { std::string subString(sSubName); if (pObj->isDerivedFrom() || pObj->isDerivedFrom() || (strstr(pObj->getNameInDocument(), "Plane") && pObj->isDerivedFrom())) { return true; // reference is an app::link or a part::feature or some subobject } else if (pObj->isDerivedFrom() || pObj->isDerivedFrom()) { bool isFace = false; //will be true if user selected face subobject or if object only has 1 face bool isEdge = false; //will be true if user selected edge subobject or if object only has 1 edge TopoDS_Shape shape; if (subString.length() > 0){ shape = Part::Feature::getTopoShape(pObj, Part::ShapeOption::NeedSubElement | Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform, sSubName).getShape(); if (strstr(subString.c_str(), "Face")){ isFace = true; //was face subobject, e.g. Face3 } else { if (strstr(subString.c_str(), "Edge")){ isEdge = true; //was edge subobject, e.g. Edge7 } } } else { //no subobjects were selected, so this is entire shape of feature shape = Part::Feature::getShape(pObj, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform); } // if there is only 1 face or 1 edge, then we don't need to force the user to select that face or edge // instead we can infer what was intended int faceCount = Part::TopoShape(shape).countSubShapes(TopAbs_FACE); int edgeCount = Part::TopoShape(shape).countSubShapes(TopAbs_EDGE); TopoDS_Face face; TopoDS_Edge edge; if (isFace) { //user selected a face, so use shape to get the TopoDS::Face face = TopoDS::Face(shape); } else { if (faceCount == 1) { //entire feature selected, but it only has 1 face, so get that face TopoDS_Shape tdface = Part::TopoShape(shape).getSubShape(std::string("Face1").c_str()); face = TopoDS::Face(tdface); isFace = true; } } if (!isFace && isEdge){ //don't bother with edge if we already have a face to work with edge = TopoDS::Edge(shape); //isEdge means an edge was selected } else { if (edgeCount == 1){ //we don't have a face yet and there were no edges in the subobject selection //but since this object only has 1 edge, we use it TopoDS_Shape tdedge = Part::TopoShape(shape).getSubShape(std::string("Edge1").c_str()); edge = TopoDS::Edge(tdedge); isEdge = true; } } if (isFace && face.IsNull()) { //ensure we have a good face to work with return false; } if (isEdge && edge.IsNull()){ //ensure we have a good edge to work with return false; } if (!isFace && !isEdge){ return false; } if (isFace) { BRepAdaptor_Surface adapt(face); if (adapt.GetType() != GeomAbs_Plane){ return false; } return true; } else { if (isEdge){ BRepAdaptor_Curve curve(edge); if (!(curve.GetType() == GeomAbs_Circle)) { return false; } return true; } } } //end of if(derived from part::feature) return true; }//end of allow() }; //end of class }; //end of namespace block /* TRANSLATOR PartGui::Mirroring */ Mirroring::Mirroring(QWidget* parent) : QWidget(parent), ui(new Ui_Mirroring) { ui->setupUi(this); constexpr double max = std::numeric_limits::max(); ui->baseX->setRange(-max, max); ui->baseY->setRange(-max, max); ui->baseZ->setRange(-max, max); ui->baseX->setUnit(Base::Unit::Length); ui->baseY->setUnit(Base::Unit::Length); ui->baseZ->setUnit(Base::Unit::Length); findShapes(); Gui::ItemViewSelection sel(ui->shapes); sel.applyFrom(Gui::Selection().getObjectsOfType(Part::Feature::getClassTypeId())); sel.applyFrom(Gui::Selection().getObjectsOfType(App::Link::getClassTypeId())); sel.applyFrom(Gui::Selection().getObjectsOfType(App::Part::getClassTypeId())); connect(ui->selectButton, &QPushButton::clicked, this, &Mirroring::onSelectButtonClicked); MirrorPlaneSelection* gate = new MirrorPlaneSelection(); Gui::Selection().addSelectionGate(gate); } /* * Destroys the object and frees any allocated resources */ Mirroring::~Mirroring() = default; void Mirroring::onSelectButtonClicked(){ if (!ui->selectButton->isChecked()){ Gui::Selection().rmvSelectionGate(); ui->selectButton->setText(tr("Select reference")); } else { MirrorPlaneSelection* gate = new MirrorPlaneSelection(); Gui::Selection().addSelectionGate(gate); ui->selectButton->setText(tr("Selecting")); } } void Mirroring::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { ui->retranslateUi(this); } QWidget::changeEvent(e); } void Mirroring::onSelectionChanged(const Gui::SelectionChanges &msg) { if (ui->selectButton->isChecked()) { if (msg.Type == Gui::SelectionChanges::AddSelection) { std::string objName(msg.pObjectName); std::string subName(msg.pSubName); std::stringstream refStr; refStr << objName << " : [" << subName << "]"; ui->referenceLineEdit->setText(QLatin1String(refStr.str().c_str())); ui->comboBox->setCurrentIndex(3); } } } void Mirroring::findShapes() { App::Document* activeDoc = App::GetApplication().getActiveDocument(); if (!activeDoc) return; Gui::Document* activeGui = Gui::Application::Instance->getDocument(activeDoc); if (!activeGui) return; this->document = QString::fromLatin1(activeDoc->getName()); std::vector objs = activeDoc->getObjectsOfType(); for (auto obj : objs) { Part::TopoShape shape = Part::Feature::getTopoShape(obj, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform); if (!shape.isNull()) { QString label = QString::fromUtf8(obj->Label.getValue()); QString name = QString::fromLatin1(obj->getNameInDocument()); QTreeWidgetItem* child = new QTreeWidgetItem(); child->setText(0, label); child->setToolTip(0, label); child->setData(0, Qt::UserRole, name); Gui::ViewProvider* vp = activeGui->getViewProvider(obj); if (vp) child->setIcon(0, vp->getIcon()); ui->shapes->addTopLevelItem(child); } } } bool Mirroring::reject() { Gui::Selection().rmvSelectionGate(); return true; } bool Mirroring::accept() { if (ui->shapes->selectedItems().isEmpty()) { QMessageBox::critical(this, windowTitle(), tr("Select a shape for mirroring.")); return false; } App::Document* activeDoc = App::GetApplication().getDocument((const char*)this->document.toLatin1()); if (!activeDoc) { QMessageBox::critical(this, windowTitle(), tr("No such document '%1'.").arg(this->document)); return false; } Gui::WaitCursor wc; unsigned int count = activeDoc->countObjectsOfType(); activeDoc->openTransaction("Mirroring"); QString shape, label, selectionString; QRegularExpression rx(QString::fromLatin1(R"( \(Mirror #\d+\)$)")); QList items = ui->shapes->selectedItems(); float normx=0, normy=0, normz=0; int index = ui->comboBox->currentIndex(); std::string selection(""); //set MirrorPlane property to empty string unless //user has selected Use selected reference in combobox if (index == 0){ normz = 1.0f; } else if (index == 1){ normy = 1.0f; } else if (index == 2){ normx = 1.0f; } else if (index == 3){ //use selected reference std::vector selobjs = Gui::Selection().getSelectionEx(); if (selobjs.size() == 1) { selection = selobjs[0].getAsPropertyLinkSubString(); } } double basex = ui->baseX->value().getValue(); double basey = ui->baseY->value().getValue(); double basez = ui->baseZ->value().getValue(); for (auto item : items) { shape = item->data(0, Qt::UserRole).toString(); std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(item->text(0).toUtf8()); label = QString::fromStdString(escapedstr); selectionString = QString::fromStdString(selection); // if we already have the suffix " (Mirror #)" remove it int pos = label.indexOf(rx); if (pos > -1) label = label.left(pos); label.append(QStringLiteral(" (Mirror #%1)").arg(++count)); QString code = QStringLiteral( "__doc__=FreeCAD.getDocument(\"%1\")\n" "__doc__.addObject(\"Part::Mirroring\")\n" "__doc__.ActiveObject.Source=__doc__.getObject(\"%2\")\n" "__doc__.ActiveObject.Label=u\"%3\"\n" "__doc__.ActiveObject.Normal=(%4,%5,%6)\n" "__doc__.ActiveObject.Base=(%7,%8,%9)\n" "__doc__.ActiveObject.MirrorPlane=(%10)\n" "del __doc__") .arg(this->document, shape, label) .arg(normx).arg(normy).arg(normz) .arg(basex).arg(basey).arg(basez) .arg(selectionString); Gui::Command::runCommand(Gui::Command::App, code.toLatin1()); QByteArray from = shape.toLatin1(); Gui::Command::copyVisual("ActiveObject", "ShapeAppearance", from); Gui::Command::copyVisual("ActiveObject", "LineColor", from); Gui::Command::copyVisual("ActiveObject", "PointColor", from); } activeDoc->commitTransaction(); activeDoc->recompute(); Gui::Selection().rmvSelectionGate(); return true; } // --------------------------------------- TaskMirroring::TaskMirroring() { widget = new Mirroring(); addTaskBox(Gui::BitmapFactory().pixmap("Part_Mirror.svg"), widget, false); } bool TaskMirroring::accept() { return widget->accept(); } bool TaskMirroring::reject() { return widget->reject(); } #include "moc_Mirroring.cpp"