// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2008 Jürgen Riegel * * * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CrossSections.h" #include "TaskCurveOnMesh.h" #include "Tessellation.h" using namespace std; //=========================================================================== // MeshPart_Mesher //=========================================================================== DEF_STD_CMD_A(CmdMeshPartMesher) CmdMeshPartMesher::CmdMeshPartMesher() : Command("MeshPart_Mesher") { sAppModule = "MeshPart"; sGroup = QT_TR_NOOP("Mesh"); sMenuText = QT_TR_NOOP("Mesh From Shape"); sToolTipText = QT_TR_NOOP("Tessellate shape"); sWhatsThis = "MeshPart_Mesher"; sStatusTip = sToolTipText; } void CmdMeshPartMesher::activated(int) { Gui::Control().showDialog(new MeshPartGui::TaskTessellation()); } bool CmdMeshPartMesher::isActive() { return (hasActiveDocument() && !Gui::Control().activeDialog()); } //-------------------------------------------------------------------------------------- DEF_STD_CMD_A(CmdMeshPartTrimByPlane) CmdMeshPartTrimByPlane::CmdMeshPartTrimByPlane() : Command("MeshPart_TrimByPlane") { sAppModule = "Mesh"; sGroup = QT_TR_NOOP("Mesh"); sMenuText = QT_TR_NOOP("Trim Mesh"); sToolTipText = QT_TR_NOOP("Trims a mesh with a plane"); sStatusTip = sToolTipText; } void CmdMeshPartTrimByPlane::activated(int) { Base::Type partType = Base::Type::fromName("Part::Plane"); std::vector plane = getSelection().getObjectsOfType(partType); if (plane.empty()) { QMessageBox::warning( Gui::getMainWindow(), qApp->translate("MeshPart_TrimByPlane", "Select plane"), qApp->translate("MeshPart_TrimByPlane", "Select a plane to trim the mesh with.") ); return; } QMessageBox msgBox(Gui::getMainWindow()); msgBox.setIcon(QMessageBox::Question); msgBox.setWindowTitle(qApp->translate("MeshPart_TrimByPlane", "Trim With Plane")); msgBox.setText(qApp->translate("MeshPart_TrimByPlane", "Select the side to keep")); QPushButton* inner = msgBox.addButton(qApp->translate("MeshPart_TrimByPlane", "Below"), QMessageBox::ActionRole); QPushButton* outer = msgBox.addButton(qApp->translate("MeshPart_TrimByPlane", "Above"), QMessageBox::ActionRole); QPushButton* split = msgBox.addButton(qApp->translate("MeshPart_TrimByPlane", "Split"), QMessageBox::ActionRole); msgBox.addButton(QMessageBox::Cancel); msgBox.setDefaultButton(inner); msgBox.exec(); QAbstractButton* click = msgBox.clickedButton(); Gui::SelectionRole role; if (inner == click) { role = Gui::SelectionRole::Inner; } else if (outer == click) { role = Gui::SelectionRole::Outer; } else if (split == click) { role = Gui::SelectionRole::Split; } else { // abort return; } Base::Placement plnPlacement = static_cast(plane.front())->Placement.getValue(); openCommand(QT_TRANSLATE_NOOP("Command", "Trim with plane")); std::vector docObj = Gui::Selection().getObjectsOfType( Mesh::Feature::getClassTypeId() ); for (auto it : docObj) { Base::Vector3d normal(0, 0, 1); plnPlacement.getRotation().multVec(normal, normal); Base::Vector3d base = plnPlacement.getPosition(); Mesh::MeshObject* mesh = static_cast(it)->Mesh.startEditing(); Base::Vector3f plnBase = Base::convertTo(base); Base::Vector3f plnNormal = Base::convertTo(normal); if (role == Gui::SelectionRole::Inner) { mesh->trimByPlane(plnBase, plnNormal); static_cast(it)->Mesh.finishEditing(); } else if (role == Gui::SelectionRole::Outer) { mesh->trimByPlane(plnBase, -plnNormal); static_cast(it)->Mesh.finishEditing(); } else if (role == Gui::SelectionRole::Split) { Mesh::MeshObject copy(*mesh); mesh->trimByPlane(plnBase, plnNormal); static_cast(it)->Mesh.finishEditing(); copy.trimByPlane(plnBase, -plnNormal); App::Document* doc = it->getDocument(); Mesh::Feature* fea = doc->addObject(); fea->Label.setValue(it->Label.getValue()); Mesh::MeshObject* feamesh = fea->Mesh.startEditing(); feamesh->swap(copy); fea->Mesh.finishEditing(); } it->purgeTouched(); } commitCommand(); } bool CmdMeshPartTrimByPlane::isActive() { // Check for the selected mesh feature (all Mesh types) return getSelection().countObjectsOfType() == 1; } //=========================================================================== // MeshPart_Section //=========================================================================== DEF_STD_CMD_A(CmdMeshPartSection) CmdMeshPartSection::CmdMeshPartSection() : Command("MeshPart_SectionByPlane") { sAppModule = "MeshPart"; sGroup = QT_TR_NOOP("Mesh"); sMenuText = QT_TR_NOOP("Section"); sToolTipText = QT_TR_NOOP("Creates a section from a mesh and plane"); sWhatsThis = "MeshPart_Section"; sStatusTip = sToolTipText; } void CmdMeshPartSection::activated(int) { Base::Type partType = Base::Type::fromName("Part::Plane"); std::vector plane = getSelection().getObjectsOfType(partType); if (plane.empty()) { QMessageBox::warning( Gui::getMainWindow(), qApp->translate("MeshPart_Section", "Select plane"), qApp->translate("MeshPart_Section", "Select a plane to section the mesh with.") ); return; } Base::Placement plm = static_cast(plane.front())->Placement.getValue(); Base::Vector3d normal(0, 0, 1); plm.getRotation().multVec(normal, normal); Base::Vector3d base = plm.getPosition(); openCommand(QT_TRANSLATE_NOOP("Command", "Section with plane")); std::vector docObj = Gui::Selection().getObjectsOfType( Mesh::Feature::getClassTypeId() ); Mesh::MeshObject::TPlane tplane; tplane.first = Base::convertTo(base); tplane.second = Base::convertTo(normal); std::vector sections; sections.push_back(tplane); Py::Module partModule(PyImport_ImportModule("Part"), true); Py::Callable makeWire(partModule.getAttr("makePolygon")); Py::Module appModule(PyImport_ImportModule("FreeCAD"), true); Py::Callable addObject(appModule.getAttr("ActiveDocument").getAttr("addObject")); for (auto it : docObj) { const Mesh::MeshObject* mesh = static_cast(it)->Mesh.getValuePtr(); std::vector polylines; const float minSectionLength = 1e-7F; mesh->crossSections(sections, polylines, minSectionLength); for (const auto& it2 : polylines) { for (const auto& it3 : it2) { Py::Tuple arg(1); Py::List list; for (auto it4 : it3) { Py::Tuple pnt(3); pnt.setItem(0, Py::Float(it4.x)); pnt.setItem(1, Py::Float(it4.y)); pnt.setItem(2, Py::Float(it4.z)); list.append(pnt); } arg.setItem(0, list); Py::Object wire = makeWire.apply(arg); Py::Tuple arg2(2); arg2.setItem(0, Py::String("Part::Feature")); arg2.setItem(1, Py::String("Section")); Py::Object obj = addObject.apply(arg2); obj.setAttr("Shape", wire); } } } updateActive(); commitCommand(); } bool CmdMeshPartSection::isActive() { // Check for the selected mesh feature (all Mesh types) return getSelection().countObjectsOfType() == 1; } //=========================================================================== // MeshPart_CrossSections //=========================================================================== DEF_STD_CMD_A(CmdMeshPartCrossSections) CmdMeshPartCrossSections::CmdMeshPartCrossSections() : Command("MeshPart_CrossSections") { sAppModule = "MeshPart"; sGroup = QT_TR_NOOP("MeshPart"); sMenuText = QT_TR_NOOP("Cross-Sections"); sToolTipText = QT_TR_NOOP("Applies cross-sections to the mesh"); sWhatsThis = "MeshPart_CrossSections"; sStatusTip = sToolTipText; // sPixmap = "MeshPart_CrossSections"; } void CmdMeshPartCrossSections::activated(int iMsg) { Q_UNUSED(iMsg); Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); if (!dlg) { std::vector obj = Gui::Selection().getObjectsOfType( Mesh::Feature::getClassTypeId() ); Base::BoundBox3d bbox; for (auto it : obj) { bbox.Add(static_cast(it)->Mesh.getBoundingBox()); } dlg = new MeshPartGui::TaskCrossSections(bbox); } Gui::Control().showDialog(dlg); } bool CmdMeshPartCrossSections::isActive() { return (Gui::Selection().countObjectsOfType() > 0 && !Gui::Control().activeDialog()); } DEF_STD_CMD_A(CmdMeshPartCurveOnMesh) CmdMeshPartCurveOnMesh::CmdMeshPartCurveOnMesh() : Command("MeshPart_CurveOnMesh") { sAppModule = "MeshPart"; sGroup = QT_TR_NOOP("Mesh"); sMenuText = QT_TR_NOOP("Curve on Mesh"); sToolTipText = QT_TR_NOOP("Creates an approximated curve on top of a mesh object"); sWhatsThis = "MeshPart_CurveOnMesh"; sStatusTip = sToolTipText; sPixmap = "MeshPart_CurveOnMesh"; } void CmdMeshPartCurveOnMesh::activated(int) { Gui::Document* doc = getActiveGuiDocument(); std::list mdis = doc->getMDIViewsOfType(Gui::View3DInventor::getClassTypeId()); if (mdis.empty()) { return; } Gui::Control().showDialog( new MeshPartGui::TaskCurveOnMesh(static_cast(mdis.front())) ); } bool CmdMeshPartCurveOnMesh::isActive() { if (Gui::Control().activeDialog()) { return false; } // Check for the selected mesh feature (all Mesh types) App::Document* doc = App::GetApplication().getActiveDocument(); return doc && doc->countObjectsOfType() > 0; } void CreateMeshPartCommands() { Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager(); rcCmdMgr.addCommand(new CmdMeshPartMesher()); rcCmdMgr.addCommand(new CmdMeshPartTrimByPlane()); rcCmdMgr.addCommand(new CmdMeshPartSection()); rcCmdMgr.addCommand(new CmdMeshPartCrossSections()); rcCmdMgr.addCommand(new CmdMeshPartCurveOnMesh()); }