// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2019 Manuel Apeltauer, direkt cnc-systeme GmbH * * * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "DlgProjectionOnSurface.h" #include "ui_DlgProjectionOnSurface.h" #include "ViewProviderExt.h" using namespace PartGui; namespace { ////////////////////////////////////////////////////////////////////////// class EdgeSelection: public Gui::SelectionFilterGate { public: bool canSelect = false; EdgeSelection() : Gui::SelectionFilterGate(nullPointer()) {} bool allow(App::Document* /*pDoc*/, App::DocumentObject* iPObj, const char* sSubName) override { auto aPart = dynamic_cast(iPObj); if (!aPart) { return false; } if (!sSubName) { return false; } std::string subName(sSubName); if (subName.empty()) { return false; } auto subShape = aPart->Shape.getShape().getSubShape(sSubName); if (subShape.IsNull()) { return false; } auto type = subShape.ShapeType(); return (type == TopAbs_EDGE); } }; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// class FaceSelection: public Gui::SelectionFilterGate { public: bool canSelect = false; FaceSelection() : Gui::SelectionFilterGate(nullPointer()) {} bool allow(App::Document* /*pDoc*/, App::DocumentObject* iPObj, const char* sSubName) override { auto aPart = dynamic_cast(iPObj); if (!aPart) { return false; } if (!sSubName) { return false; } std::string subName(sSubName); if (subName.empty()) { return false; } auto subShape = aPart->Shape.getShape().getSubShape(sSubName, true); if (subShape.IsNull()) { return false; } auto type = subShape.ShapeType(); return (type == TopAbs_FACE); } }; ////////////////////////////////////////////////////////////////////////// } // namespace DlgProjectionOnSurface::DlgProjectionOnSurface(QWidget* parent) : QWidget(parent) , ui(new Ui::DlgProjectionOnSurface) , m_projectionObjectName(tr("Projection object")) , filterEdge(nullptr) , filterFace(nullptr) { ui->setupUi(this); setupConnections(); ui->pushButtonAddEdge->setCheckable(true); ui->pushButtonAddFace->setCheckable(true); ui->pushButtonAddProjFace->setCheckable(true); ui->pushButtonAddWire->setCheckable(true); m_guiObjectVec.push_back(ui->pushButtonAddEdge); m_guiObjectVec.push_back(ui->pushButtonAddFace); m_guiObjectVec.push_back(ui->pushButtonAddProjFace); m_guiObjectVec.push_back(ui->pushButtonDirX); m_guiObjectVec.push_back(ui->pushButtonDirY); m_guiObjectVec.push_back(ui->pushButtonDirZ); m_guiObjectVec.push_back(ui->pushButtonGetCurrentCamDir); m_guiObjectVec.push_back(ui->radioButtonShowAll); m_guiObjectVec.push_back(ui->radioButtonFaces); m_guiObjectVec.push_back(ui->radioButtonEdges); m_guiObjectVec.push_back(ui->pushButtonAddWire); get_camera_direction(); disable_ui_elements(m_guiObjectVec, ui->pushButtonAddProjFace); m_partDocument = App::GetApplication().getActiveDocument(); if (!m_partDocument) { throw Base::ValueError(tr("No active document").toStdString()); } this->attachDocument(m_partDocument); m_partDocument->openTransaction("Project on surface"); m_projectionObject = m_partDocument->addObject("Projection Object"); if (!m_projectionObject) { throw Base::ValueError(tr("Cannot create a projection object").toStdString()); } m_projectionObject->Label.setValue(std::string(m_projectionObjectName.toUtf8()).c_str()); onRadioButtonShowAllClicked(); m_lastDepthVal = ui->doubleSpinBoxSolidDepth->value(); } DlgProjectionOnSurface::~DlgProjectionOnSurface() { delete ui; for (const auto& it : m_projectionSurfaceVec) { try { higlight_object(it.partFeature, it.partName, false, 0); } catch (Standard_NoSuchObject& e) { Base::Console().warning( "DlgProjectionOnSurface::~DlgProjectionOnSurface: %s", e.GetMessageString() ); } auto vp = dynamic_cast( Gui::Application::Instance->getViewProvider(it.partFeature) ); if (vp) { vp->Selectable.setValue(it.is_selectable); vp->Transparency.setValue(it.transparency); } } for (const auto& it : m_shapeVec) { try { higlight_object(it.partFeature, it.partName, false, 0); } catch (Standard_NoSuchObject& e) { Base::Console().warning( "DlgProjectionOnSurface::~DlgProjectionOnSurface: %s", e.GetMessageString() ); } } Gui::Selection().rmvSelectionGate(); } void PartGui::DlgProjectionOnSurface::setupConnections() { // clang-format off connect(ui->pushButtonAddFace, &QPushButton::clicked, this, &DlgProjectionOnSurface::onPushButtonAddFaceClicked); connect(ui->pushButtonAddEdge, &QPushButton::clicked, this, &DlgProjectionOnSurface::onPushButtonAddEdgeClicked); connect(ui->pushButtonGetCurrentCamDir, &QPushButton::clicked, this, &DlgProjectionOnSurface::onPushButtonGetCurrentCamDirClicked); connect(ui->pushButtonDirX, &QPushButton::clicked, this, &DlgProjectionOnSurface::onPushButtonDirXClicked); connect(ui->pushButtonDirY, &QPushButton::clicked, this, &DlgProjectionOnSurface::onPushButtonDirYClicked); connect(ui->pushButtonDirZ, &QPushButton::clicked, this, &DlgProjectionOnSurface::onPushButtonDirZClicked); connect(ui->pushButtonAddProjFace, &QPushButton::clicked, this, &DlgProjectionOnSurface::onPushButtonAddProjFaceClicked); connect(ui->radioButtonShowAll, &QRadioButton::clicked, this, &DlgProjectionOnSurface::onRadioButtonShowAllClicked); connect(ui->radioButtonFaces, &QRadioButton::clicked, this, &DlgProjectionOnSurface::onRadioButtonFacesClicked); connect(ui->radioButtonEdges, &QRadioButton::clicked, this, &DlgProjectionOnSurface::onRadioButtonEdgesClicked); connect(ui->doubleSpinBoxExtrudeHeight, qOverload(&QDoubleSpinBox::valueChanged), this, &DlgProjectionOnSurface::onDoubleSpinBoxExtrudeHeightValueChanged); connect(ui->pushButtonAddWire, &QPushButton::clicked, this, &DlgProjectionOnSurface::onPushButtonAddWireClicked); connect(ui->doubleSpinBoxSolidDepth, qOverload(&QDoubleSpinBox::valueChanged), this, &DlgProjectionOnSurface::onDoubleSpinBoxSolidDepthValueChanged); // clang-format on } void PartGui::DlgProjectionOnSurface::slotDeletedDocument(const App::Document& Doc) { if (m_partDocument == &Doc) { m_partDocument = nullptr; m_projectionObject = nullptr; } } void PartGui::DlgProjectionOnSurface::slotDeletedObject(const App::DocumentObject& Obj) { if (m_projectionObject == &Obj) { m_projectionObject = nullptr; } } void PartGui::DlgProjectionOnSurface::apply() { if (m_partDocument) { m_partDocument->commitTransaction(); } } void PartGui::DlgProjectionOnSurface::reject() { if (m_partDocument) { m_partDocument->abortTransaction(); } } void PartGui::DlgProjectionOnSurface::onPushButtonAddFaceClicked() { if (ui->pushButtonAddFace->isChecked()) { m_currentSelection = "add_face"; disable_ui_elements(m_guiObjectVec, ui->pushButtonAddFace); if (!filterFace) { filterFace = new FaceSelection(); Gui::Selection().addSelectionGate(filterFace); } } else { m_currentSelection = ""; enable_ui_elements(m_guiObjectVec, nullptr); Gui::Selection().rmvSelectionGate(); filterFace = nullptr; } } void PartGui::DlgProjectionOnSurface::onPushButtonAddEdgeClicked() { if (ui->pushButtonAddEdge->isChecked()) { m_currentSelection = "add_edge"; disable_ui_elements(m_guiObjectVec, ui->pushButtonAddEdge); if (!filterEdge) { filterEdge = new EdgeSelection(); Gui::Selection().addSelectionGate(filterEdge); } ui->radioButtonEdges->setChecked(true); onRadioButtonEdgesClicked(); } else { m_currentSelection = ""; enable_ui_elements(m_guiObjectVec, nullptr); Gui::Selection().rmvSelectionGate(); filterEdge = nullptr; } } void PartGui::DlgProjectionOnSurface::onPushButtonGetCurrentCamDirClicked() { get_camera_direction(); } void PartGui::DlgProjectionOnSurface::onPushButtonDirXClicked() { set_xyz_dir_spinbox(ui->doubleSpinBoxDirX); } void PartGui::DlgProjectionOnSurface::onPushButtonDirYClicked() { set_xyz_dir_spinbox(ui->doubleSpinBoxDirY); } void PartGui::DlgProjectionOnSurface::onPushButtonDirZClicked() { set_xyz_dir_spinbox(ui->doubleSpinBoxDirZ); } void PartGui::DlgProjectionOnSurface::onSelectionChanged(const Gui::SelectionChanges& msg) { if (msg.Type == Gui::SelectionChanges::AddSelection) { if (m_currentSelection == "add_face" || m_currentSelection == "add_edge" || m_currentSelection == "add_wire") { store_current_selected_parts(m_shapeVec, 0xff00ff00); create_projection_wire(m_shapeVec); create_projection_face_from_wire(m_shapeVec); create_face_extrude(m_shapeVec); show_projected_shapes(m_shapeVec); } else if (m_currentSelection == "add_projection_surface") { m_projectionSurfaceVec.clear(); store_current_selected_parts(m_projectionSurfaceVec, 0xffff0000); if (!m_projectionSurfaceVec.empty()) { auto vp = dynamic_cast( Gui::Application::Instance->getViewProvider(m_projectionSurfaceVec.back().partFeature) ); if (vp) { vp->Selectable.setValue(false); vp->Transparency.setValue(90); } } ui->pushButtonAddProjFace->setChecked(false); onPushButtonAddProjFaceClicked(); } } } void PartGui::DlgProjectionOnSurface::get_camera_direction() { auto mainWindow = Gui::getMainWindow(); auto mdiObject = dynamic_cast(mainWindow->activeWindow()); if (!mdiObject) { return; } auto camerRotation = mdiObject->getViewer()->getCameraOrientation(); SbVec3f lookAt(0, 0, -1); camerRotation.multVec(lookAt, lookAt); float valX {}; float valY {}; float valZ {}; lookAt.getValue(valX, valY, valZ); ui->doubleSpinBoxDirX->setValue(valX); ui->doubleSpinBoxDirY->setValue(valY); ui->doubleSpinBoxDirZ->setValue(valZ); } void PartGui::DlgProjectionOnSurface::store_current_selected_parts( std::vector& iStoreVec, unsigned int iColor ) { if (!m_partDocument) { return; } std::vector selObj = Gui::Selection().getSelectionEx(); if (!selObj.empty()) { for (auto it = selObj.begin(); it != selObj.end(); ++it) { auto aPart = it->getObject(); if (!aPart) { continue; } if (aPart) { SShapeStore currentShapeStore; currentShapeStore.inputShape = aPart->Shape.getShape().getShape(); currentShapeStore.partFeature = aPart; currentShapeStore.partName = aPart->getNameInDocument(); auto vp = dynamic_cast( Gui::Application::Instance->getViewProvider(aPart) ); if (vp) { currentShapeStore.is_selectable = vp->Selectable.getValue(); currentShapeStore.transparency = vp->Transparency.getValue(); } if (!it->getSubNames().empty()) { auto parentShape = currentShapeStore.inputShape; for (const auto& itName : selObj.front().getSubNames()) { auto currentShape = aPart->Shape.getShape().getSubShape(itName.c_str()); transform_shape_to_global_position(currentShape, aPart); currentShapeStore.inputShape = currentShape; currentShapeStore.partName = itName; auto store = store_part_in_vector(currentShapeStore, iStoreVec); higlight_object(aPart, itName, store, iColor); store_wire_in_vector(currentShapeStore, parentShape, iStoreVec, iColor); } } else { transform_shape_to_global_position( currentShapeStore.inputShape, currentShapeStore.partFeature ); auto store = store_part_in_vector(currentShapeStore, iStoreVec); higlight_object(aPart, aPart->Shape.getName(), store, iColor); } Gui::Selection().clearSelection(m_partDocument->getName()); Gui::Selection().rmvPreselect(); } } } } bool PartGui::DlgProjectionOnSurface::store_part_in_vector( SShapeStore& iCurrentShape, std::vector& iStoreVec ) { if (iCurrentShape.inputShape.IsNull()) { return false; } auto currentType = iCurrentShape.inputShape.ShapeType(); for (auto it = iStoreVec.begin(); it != iStoreVec.end(); ++it) { if (currentType == TopAbs_FACE) { if (it->aFace.IsSame(iCurrentShape.inputShape)) { iStoreVec.erase(it); return false; } } else if (currentType == TopAbs_EDGE) { if (it->aEdge.IsSame(iCurrentShape.inputShape)) { iStoreVec.erase(it); return false; } } } if (currentType == TopAbs_FACE) { iCurrentShape.aFace = TopoDS::Face(iCurrentShape.inputShape); } else if (currentType == TopAbs_EDGE) { iCurrentShape.aEdge = TopoDS::Edge(iCurrentShape.inputShape); } auto valX = ui->doubleSpinBoxDirX->value(); auto valY = ui->doubleSpinBoxDirY->value(); auto valZ = ui->doubleSpinBoxDirZ->value(); iCurrentShape.aProjectionDir = gp_Dir(valX, valY, valZ); if (!m_projectionSurfaceVec.empty()) { iCurrentShape.surfaceToProject = m_projectionSurfaceVec.front().aFace; } iStoreVec.push_back(iCurrentShape); return true; } void PartGui::DlgProjectionOnSurface::create_projection_wire(std::vector& iCurrentShape) { try { if (iCurrentShape.empty()) { return; } for (auto& itCurrentShape : iCurrentShape) { if (m_projectionSurfaceVec.empty()) { continue; }; if (!itCurrentShape.aProjectedEdgeVec.empty()) { continue; }; if (!itCurrentShape.aProjectedFace.IsNull()) { continue; }; if (!itCurrentShape.aProjectedWireVec.empty()) { continue; }; if (!itCurrentShape.aFace.IsNull()) { get_all_wire_from_face(itCurrentShape); for (const auto& itWire : itCurrentShape.aWireVec) { BRepProj_Projection aProjection( itWire, itCurrentShape.surfaceToProject, itCurrentShape.aProjectionDir ); double minDistance = std::numeric_limits::max(); TopoDS_Wire wireToTake; for (; aProjection.More(); aProjection.Next()) { auto it = aProjection.Current(); BRepExtrema_DistShapeShape distanceMeasure(it, itCurrentShape.aFace); distanceMeasure.Perform(); auto currentDistance = distanceMeasure.Value(); if (currentDistance > minDistance) { continue; } wireToTake = it; minDistance = currentDistance; } auto aWire = sort_and_heal_wire(wireToTake, itCurrentShape.surfaceToProject); itCurrentShape.aProjectedWireVec.push_back(aWire); } } else if (!itCurrentShape.aEdge.IsNull()) { BRepProj_Projection aProjection( itCurrentShape.aEdge, itCurrentShape.surfaceToProject, itCurrentShape.aProjectionDir ); double minDistance = std::numeric_limits::max(); TopoDS_Wire wireToTake; for (; aProjection.More(); aProjection.Next()) { auto it = aProjection.Current(); BRepExtrema_DistShapeShape distanceMeasure(it, itCurrentShape.aEdge); distanceMeasure.Perform(); auto currentDistance = distanceMeasure.Value(); if (currentDistance > minDistance) { continue; } wireToTake = it; minDistance = currentDistance; } for (TopExp_Explorer aExplorer(wireToTake, TopAbs_EDGE); aExplorer.More(); aExplorer.Next()) { itCurrentShape.aProjectedEdgeVec.push_back(TopoDS::Edge(aExplorer.Current())); } } } } catch (const Standard_Failure& error) { std::stringstream ssOcc; error.Print(ssOcc); throw Base::ValueError(ssOcc.str().c_str()); } } TopoDS_Shape PartGui::DlgProjectionOnSurface::create_compound(const std::vector& iShapeVec) { if (iShapeVec.empty()) { return {}; } TopoDS_Compound aCompound; TopoDS_Builder aBuilder; aBuilder.MakeCompound(aCompound); for (const auto& it : iShapeVec) { if (m_currentShowType == "edges") { for (const auto& it2 : it.aProjectedEdgeVec) { aBuilder.Add(aCompound, it2); } for (const auto& it2 : it.aProjectedWireVec) { aBuilder.Add(aCompound, it2); } } else if (m_currentShowType == "faces") { if (it.aProjectedFace.IsNull()) { for (const auto& it2 : it.aProjectedWireVec) { if (!it2.IsNull()) { aBuilder.Add(aCompound, it2); } } } else { aBuilder.Add(aCompound, it.aProjectedFace); } } else if (m_currentShowType == "all") { if (!it.aProjectedSolid.IsNull()) { aBuilder.Add(aCompound, it.aProjectedSolid); } else if (!it.aProjectedFace.IsNull()) { aBuilder.Add(aCompound, it.aProjectedFace); } else if (!it.aProjectedWireVec.empty()) { for (const auto& itWire : it.aProjectedWireVec) { if (itWire.IsNull()) { continue; } aBuilder.Add(aCompound, itWire); } } else if (!it.aProjectedEdgeVec.empty()) { for (const auto& itEdge : it.aProjectedEdgeVec) { if (itEdge.IsNull()) { continue; } aBuilder.Add(aCompound, itEdge); } } } } return {std::move(aCompound)}; } void PartGui::DlgProjectionOnSurface::show_projected_shapes( const std::vector& iShapeStoreVec ) { if (!m_projectionObject) { return; } auto aCompound = create_compound(iShapeStoreVec); if (aCompound.IsNull()) { if (!m_partDocument) { return; } m_projectionObject->Shape.setValue(TopoDS_Shape()); return; } auto currentPlacement = m_projectionObject->Placement.getValue(); m_projectionObject->Shape.setValue(aCompound); m_projectionObject->Placement.setValue(currentPlacement); // set color auto vp = dynamic_cast( Gui::Application::Instance->getViewProvider(m_projectionObject) ); if (vp) { const unsigned int color = 0x8ae23400; vp->LineColor.setValue(color); vp->ShapeAppearance.setDiffuseColor(Base::Color(color)); vp->PointColor.setValue(color); vp->Transparency.setValue(0); } } void PartGui::DlgProjectionOnSurface::disable_ui_elements( const std::vector& iObjectVec, QWidget* iExceptThis ) { for (auto it : iObjectVec) { if (!it) { continue; } if (it == iExceptThis) { continue; } it->setDisabled(true); } } void PartGui::DlgProjectionOnSurface::enable_ui_elements( const std::vector& iObjectVec, QWidget* iExceptThis ) { for (auto it : iObjectVec) { if (!it) { continue; } if (it == iExceptThis) { continue; } it->setEnabled(true); } } void PartGui::DlgProjectionOnSurface::higlight_object( Part::Feature* iCurrentObject, const std::string& iShapeName, bool iHighlight, unsigned int iColor ) { if (!iCurrentObject) { return; } auto partenShape = iCurrentObject->Shape.getShape().getShape(); auto subShape = iCurrentObject->Shape.getShape().getSubShape(iShapeName.c_str(), true); TopoDS_Shape currentShape = subShape; if (subShape.IsNull()) { currentShape = partenShape; } auto currentShapeType = currentShape.ShapeType(); TopTools_IndexedMapOfShape anIndices; TopExp::MapShapes(partenShape, currentShapeType, anIndices); if (anIndices.IsEmpty()) { return; } if (!anIndices.Contains(currentShape)) { return; } auto index = anIndices.FindIndex(currentShape); // set color auto vp = dynamic_cast( Gui::Application::Instance->getViewProvider(iCurrentObject) ); if (vp) { std::vector colors; Base::Color defaultColor; if (currentShapeType == TopAbs_FACE) { colors = vp->ShapeAppearance.getDiffuseColors(); defaultColor = colors.front(); } else if (currentShapeType == TopAbs_EDGE) { colors = vp->LineColorArray.getValues(); defaultColor = vp->LineColor.getValue(); } if (static_cast(colors.size()) != anIndices.Extent()) { colors.resize(anIndices.Extent(), defaultColor); } if (iHighlight) { Base::Color aColor; aColor.setPackedValue(iColor); colors.at(index - 1) = aColor; } else { colors.at(index - 1) = defaultColor; } if (currentShapeType == TopAbs_FACE) { vp->ShapeAppearance.setDiffuseColors(colors); } else if (currentShapeType == TopAbs_EDGE) { vp->LineColorArray.setValues(colors); } } } void PartGui::DlgProjectionOnSurface::get_all_wire_from_face(SShapeStore& ioCurrentSahpe) { auto outerWire = ShapeAnalysis::OuterWire(ioCurrentSahpe.aFace); ioCurrentSahpe.aWireVec.push_back(outerWire); for (TopExp_Explorer aExplorer(ioCurrentSahpe.aFace, TopAbs_WIRE); aExplorer.More(); aExplorer.Next()) { auto currentWire = TopoDS::Wire(aExplorer.Current()); if (currentWire.IsSame(outerWire)) { continue; } ioCurrentSahpe.aWireVec.push_back(currentWire); } } void PartGui::DlgProjectionOnSurface::create_projection_face_from_wire( std::vector& iCurrentShape ) { try { if (iCurrentShape.empty()) { return; } for (auto& itCurrentShape : iCurrentShape) { if (itCurrentShape.aFace.IsNull()) { continue; }; if (itCurrentShape.aProjectedWireVec.empty()) { continue; }; if (!itCurrentShape.aProjectedFace.IsNull()) { continue; }; auto surface = BRep_Tool::Surface(itCurrentShape.surfaceToProject); // create a wire of all edges in parametric space on the surface of the face to // projected // --> otherwise BRepBuilderAPI_MakeFace can not make a face from the wire! for (const auto& itWireVec : itCurrentShape.aProjectedWireVec) { std::vector edgeVec; for (TopExp_Explorer aExplorer(itWireVec, TopAbs_EDGE); aExplorer.More(); aExplorer.Next()) { auto currentEdge = TopoDS::Edge(aExplorer.Current()); edgeVec.push_back(currentEdge); } if (edgeVec.empty()) { continue; } std::vector edgeInParametricSpaceVec; for (auto itEdge : edgeVec) { Standard_Real first {}; Standard_Real last {}; auto currentCurve = BRep_Tool::CurveOnSurface( TopoDS::Edge(itEdge), itCurrentShape.surfaceToProject, first, last ); if (!currentCurve) { continue; } auto edgeInParametricSpace = BRepBuilderAPI_MakeEdge(currentCurve, surface, first, last).Edge(); edgeInParametricSpaceVec.push_back(edgeInParametricSpace); } auto aWire = sort_and_heal_wire(edgeInParametricSpaceVec, itCurrentShape.surfaceToProject); itCurrentShape.aProjectedWireInParametricSpaceVec.push_back(aWire); } // try to create a face from the wires // the first wire is the otherwise // the following wires are the inside wires BRepBuilderAPI_MakeFace faceMaker; bool first = true; for (auto itWireVec : itCurrentShape.aProjectedWireInParametricSpaceVec) { if (first) { first = false; // change the wire direction, otherwise no face is created auto currentWire = TopoDS::Wire(itWireVec.Reversed()); if (itCurrentShape.surfaceToProject.Orientation() == TopAbs_REVERSED) { currentWire = itWireVec; } faceMaker = BRepBuilderAPI_MakeFace(surface, currentWire); ShapeFix_Face fix(faceMaker.Face()); fix.Perform(); auto aFace = fix.Face(); BRepCheck_Analyzer aChecker(aFace); if (!aChecker.IsValid()) { faceMaker = BRepBuilderAPI_MakeFace(surface, TopoDS::Wire(currentWire.Reversed())); } } else { // make a copy of the current face maker // if the face fails just try again with the copy TopoDS_Face tempCopy = BRepBuilderAPI_MakeFace(faceMaker.Face()).Face(); faceMaker.Add(TopoDS::Wire(itWireVec.Reversed())); ShapeFix_Face fix(faceMaker.Face()); fix.Perform(); auto aFace = fix.Face(); BRepCheck_Analyzer aChecker(aFace); if (!aChecker.IsValid()) { faceMaker = BRepBuilderAPI_MakeFace(tempCopy); faceMaker.Add(TopoDS::Wire(itWireVec)); } } } // auto doneFlag = faceMaker.IsDone(); // auto error = faceMaker.Error(); itCurrentShape.aProjectedFace = faceMaker.Face(); } } catch (const Standard_Failure& error) { std::stringstream ssOcc; error.Print(ssOcc); throw Base::ValueError(ssOcc.str().c_str()); } } TopoDS_Wire PartGui::DlgProjectionOnSurface::sort_and_heal_wire( const TopoDS_Shape& iShape, const TopoDS_Face& iFaceToProject ) { std::vector aEdgeVec; for (TopExp_Explorer aExplorer(iShape, TopAbs_EDGE); aExplorer.More(); aExplorer.Next()) { auto anEdge = TopoDS::Edge(aExplorer.Current()); aEdgeVec.push_back(anEdge); } return sort_and_heal_wire(aEdgeVec, iFaceToProject); } TopoDS_Wire PartGui::DlgProjectionOnSurface::sort_and_heal_wire( const std::vector& iEdgeVec, const TopoDS_Face& iFaceToProject ) { // try to sort and heal all wires // if the wires are not clean making a face will fail! ShapeAnalysis_FreeBounds shapeAnalyzer; Handle(TopTools_HSequenceOfShape) shapeList = new TopTools_HSequenceOfShape; Handle(TopTools_HSequenceOfShape) aWireHandle; Handle(TopTools_HSequenceOfShape) aWireWireHandle; for (const auto& it : iEdgeVec) { shapeList->Append(it); } const double tolerance = 0.0001; ShapeAnalysis_FreeBounds::ConnectEdgesToWires(shapeList, tolerance, false, aWireHandle); ShapeAnalysis_FreeBounds::ConnectWiresToWires(aWireHandle, tolerance, false, aWireWireHandle); if (!aWireWireHandle) { return {}; } for (auto it = 1; it <= aWireWireHandle->Length(); ++it) { auto aShape = TopoDS::Wire(aWireWireHandle->Value(it)); ShapeFix_Wire aWireRepair(aShape, iFaceToProject, tolerance); aWireRepair.FixAddCurve3dMode() = 1; aWireRepair.FixAddPCurveMode() = 1; aWireRepair.Perform(); // return aWireRepair.Wire(); ShapeFix_Wireframe aWireFramFix(aWireRepair.Wire()); aWireFramFix.FixWireGaps(); aWireFramFix.FixSmallEdges(); return TopoDS::Wire(aWireFramFix.Shape()); } return {}; } void PartGui::DlgProjectionOnSurface::create_face_extrude(std::vector& iCurrentShape) { try { if (iCurrentShape.empty()) { return; } auto height = ui->doubleSpinBoxExtrudeHeight->value(); for (auto& itCurrentShape : iCurrentShape) { if (itCurrentShape.aProjectedFace.IsNull()) { continue; } if (itCurrentShape.extrudeValue == height) { continue; } itCurrentShape.extrudeValue = height; if (height == 0) { itCurrentShape.aProjectedSolid.Nullify(); } else { gp_Vec directionToExtrude(itCurrentShape.aProjectionDir.XYZ()); directionToExtrude.Reverse(); directionToExtrude.Multiply(height); BRepPrimAPI_MakePrism extrude(itCurrentShape.aProjectedFace, directionToExtrude); itCurrentShape.aProjectedSolid = extrude.Shape(); } } } catch (const Standard_Failure& error) { std::stringstream ssOcc; error.Print(ssOcc); throw Base::ValueError(ssOcc.str().c_str()); } } void PartGui::DlgProjectionOnSurface::store_wire_in_vector( const SShapeStore& iCurrentShape, const TopoDS_Shape& iParentShape, std::vector& iStoreVec, unsigned int iColor ) { if (m_currentSelection != "add_wire") { return; } if (iParentShape.IsNull()) { return; } if (iCurrentShape.inputShape.IsNull()) { return; } auto currentType = iCurrentShape.inputShape.ShapeType(); if (currentType != TopAbs_EDGE) { return; } std::vector aWireVec; for (TopExp_Explorer aExplorer(iParentShape, TopAbs_WIRE); aExplorer.More(); aExplorer.Next()) { aWireVec.push_back(TopoDS::Wire(aExplorer.Current())); } std::vector edgeVec; for (const auto& it : aWireVec) { bool edgeExists = false; for (TopExp_Explorer aExplorer(it, TopAbs_EDGE); aExplorer.More(); aExplorer.Next()) { auto currentEdge = TopoDS::Edge(aExplorer.Current()); edgeVec.push_back(currentEdge); if (currentEdge.IsSame(iCurrentShape.inputShape)) { edgeExists = true; } } if (edgeExists) { break; } edgeVec.clear(); } if (edgeVec.empty()) { return; } TopTools_IndexedMapOfShape indexMap; TopExp::MapShapes(iParentShape, TopAbs_EDGE, indexMap); if (indexMap.IsEmpty()) { return; } for (const auto& it : edgeVec) { if (it.IsSame(iCurrentShape.inputShape)) { continue; } if (!indexMap.Contains(it)) { return; } auto index = indexMap.FindIndex(it); auto newEdgeObject = iCurrentShape; newEdgeObject.inputShape = it; newEdgeObject.partName = "Edge" + std::to_string(index); auto store = store_part_in_vector(newEdgeObject, iStoreVec); higlight_object(newEdgeObject.partFeature, newEdgeObject.partName, store, iColor); } } void PartGui::DlgProjectionOnSurface::set_xyz_dir_spinbox(QDoubleSpinBox* icurrentSpinBox) { auto currentVal = icurrentSpinBox->value(); auto newVal = 0.0; if (currentVal != 1.0 && currentVal != -1.0) { newVal = -1; } else if (currentVal == 1.0) { newVal = -1; } else if (currentVal == -1.0) { newVal = 1; } ui->doubleSpinBoxDirX->setValue(0); ui->doubleSpinBoxDirY->setValue(0); ui->doubleSpinBoxDirZ->setValue(0); icurrentSpinBox->setValue(newVal); } void PartGui::DlgProjectionOnSurface::transform_shape_to_global_position( TopoDS_Shape& ioShape, Part::Feature* iPart ) { auto currentPos = iPart->Placement.getValue().getPosition(); auto currentRotation = iPart->Placement.getValue().getRotation(); auto globalPlacement = iPart->globalPlacement(); auto globalPosition = globalPlacement.getPosition(); auto globalRotation = globalPlacement.getRotation(); if (currentRotation != globalRotation) { auto newRotation = globalRotation; newRotation *= currentRotation.invert(); gp_Trsf aAngleTransform; Base::Vector3d rotationAxes; double rotationAngle {}; newRotation.getRawValue(rotationAxes, rotationAngle); aAngleTransform.SetRotation( gp_Ax1( gp_Pnt(currentPos.x, currentPos.y, currentPos.z), gp_Dir(rotationAxes.x, rotationAxes.y, rotationAxes.z) ), rotationAngle ); ioShape = BRepBuilderAPI_Transform(ioShape, aAngleTransform, true).Shape(); } if (currentPos != globalPosition) { gp_Trsf aPosTransform; aPosTransform.SetTranslation( gp_Pnt(currentPos.x, currentPos.y, currentPos.z), gp_Pnt(globalPosition.x, globalPosition.y, globalPosition.z) ); ioShape = BRepBuilderAPI_Transform(ioShape, aPosTransform, true).Shape(); } } void PartGui::DlgProjectionOnSurface::onPushButtonAddProjFaceClicked() { if (ui->pushButtonAddProjFace->isChecked()) { m_currentSelection = "add_projection_surface"; disable_ui_elements(m_guiObjectVec, ui->pushButtonAddProjFace); if (!filterFace) { filterFace = new FaceSelection(); Gui::Selection().addSelectionGate(filterFace); } } else { m_currentSelection = ""; enable_ui_elements(m_guiObjectVec, nullptr); Gui::Selection().rmvSelectionGate(); filterFace = nullptr; } } void PartGui::DlgProjectionOnSurface::onRadioButtonShowAllClicked() { m_currentShowType = "all"; show_projected_shapes(m_shapeVec); } void PartGui::DlgProjectionOnSurface::onRadioButtonFacesClicked() { m_currentShowType = "faces"; show_projected_shapes(m_shapeVec); } void PartGui::DlgProjectionOnSurface::onRadioButtonEdgesClicked() { m_currentShowType = "edges"; show_projected_shapes(m_shapeVec); } void PartGui::DlgProjectionOnSurface::onDoubleSpinBoxExtrudeHeightValueChanged(double arg1) { Q_UNUSED(arg1); create_face_extrude(m_shapeVec); show_projected_shapes(m_shapeVec); } void PartGui::DlgProjectionOnSurface::onPushButtonAddWireClicked() { if (ui->pushButtonAddWire->isChecked()) { m_currentSelection = "add_wire"; disable_ui_elements(m_guiObjectVec, ui->pushButtonAddWire); if (!filterEdge) { filterEdge = new EdgeSelection(); Gui::Selection().addSelectionGate(filterEdge); } ui->radioButtonEdges->setChecked(true); onRadioButtonEdgesClicked(); } else { m_currentSelection = ""; enable_ui_elements(m_guiObjectVec, nullptr); Gui::Selection().rmvSelectionGate(); filterEdge = nullptr; } } void PartGui::DlgProjectionOnSurface::onDoubleSpinBoxSolidDepthValueChanged(double arg1) { auto valX = ui->doubleSpinBoxDirX->value(); auto valY = ui->doubleSpinBoxDirY->value(); auto valZ = ui->doubleSpinBoxDirZ->value(); auto valueToMove = arg1 - m_lastDepthVal; Base::Vector3d vectorToMove(valX, valY, valZ); vectorToMove *= valueToMove; auto placment = m_projectionObject->Placement.getValue(); placment.move(vectorToMove); m_projectionObject->Placement.setValue(placment); m_lastDepthVal = ui->doubleSpinBoxSolidDepth->value(); } // --------------------------------------- TaskProjectionOnSurface::TaskProjectionOnSurface() : widget(new DlgProjectionOnSurface()) , taskbox(new Gui::TaskView::TaskBox( Gui::BitmapFactory().pixmap("Part_ProjectionOnSurface"), widget->windowTitle(), true, nullptr )) { taskbox->groupLayout()->addWidget(widget); Content.push_back(taskbox); } bool TaskProjectionOnSurface::accept() { widget->apply(); return true; } bool TaskProjectionOnSurface::reject() { widget->reject(); return true; } void TaskProjectionOnSurface::clicked(int id) { if (id == QDialogButtonBox::Apply) { try { widget->apply(); } catch (Base::AbortException&) { } } } // ------------------------------------------------------------------------------------------------ DlgProjectOnSurface::DlgProjectOnSurface(Part::ProjectOnSurface* feature, QWidget* parent) : QWidget(parent) , ui(new Ui::DlgProjectionOnSurface) , filterEdge(nullptr) , filterFace(nullptr) , feature(feature) { ui->setupUi(this); ui->doubleSpinBoxExtrudeHeight->setValue(feature->Height.getValue()); ui->doubleSpinBoxSolidDepth->setValue(feature->Offset.getValue()); fetchDirection(); fetchMode(); setupConnections(); ui->pushButtonAddEdge->setCheckable(true); ui->pushButtonAddFace->setCheckable(true); ui->pushButtonAddProjFace->setCheckable(true); ui->pushButtonAddWire->setCheckable(true); } DlgProjectOnSurface::~DlgProjectOnSurface() { if (filterFace || filterEdge) { Gui::Selection().rmvSelectionGate(); } } void DlgProjectOnSurface::setupConnections() { // clang-format off connect(ui->pushButtonAddFace, &QPushButton::clicked, this, &DlgProjectOnSurface::onAddFaceClicked); connect(ui->pushButtonAddEdge, &QPushButton::clicked, this, &DlgProjectOnSurface::onAddEdgeClicked); connect(ui->pushButtonGetCurrentCamDir, &QPushButton::clicked, this, &DlgProjectOnSurface::onGetCurrentCamDirClicked); connect(ui->pushButtonDirX, &QPushButton::clicked, this, &DlgProjectOnSurface::onDirXClicked); connect(ui->pushButtonDirY, &QPushButton::clicked, this, &DlgProjectOnSurface::onDirYClicked); connect(ui->pushButtonDirZ, &QPushButton::clicked, this, &DlgProjectOnSurface::onDirZClicked); connect(ui->pushButtonAddProjFace, &QPushButton::clicked, this, &DlgProjectOnSurface::onAddProjFaceClicked); connect(ui->radioButtonShowAll, &QRadioButton::clicked, this, &DlgProjectOnSurface::onShowAllClicked); connect(ui->radioButtonFaces, &QRadioButton::clicked, this, &DlgProjectOnSurface::onFacesClicked); connect(ui->radioButtonEdges, &QRadioButton::clicked, this, &DlgProjectOnSurface::onEdgesClicked); connect(ui->doubleSpinBoxExtrudeHeight, qOverload(&QDoubleSpinBox::valueChanged), this, &DlgProjectOnSurface::onExtrudeHeightValueChanged); connect(ui->pushButtonAddWire, &QPushButton::clicked, this, &DlgProjectOnSurface::onAddWireClicked); connect(ui->doubleSpinBoxSolidDepth, qOverload(&QDoubleSpinBox::valueChanged), this, &DlgProjectOnSurface::onSolidDepthValueChanged); // clang-format off } void DlgProjectOnSurface::onSelectionChanged(const Gui::SelectionChanges& msg) { // clang-format off if (msg.Type == Gui::SelectionChanges::AddSelection) { if (selectionMode == SelectionMode::AddFace || selectionMode == SelectionMode::AddEdge) { addSelection(msg); } else if (selectionMode == SelectionMode::AddWire) { addWire(msg); } else if (selectionMode == SelectionMode::SupportFace) { ui->pushButtonAddProjFace->setChecked(false); setSupportFace(msg); onAddProjFaceClicked(); } } // clang-format on } void DlgProjectOnSurface::accept() { if (!feature.expired()) { auto document = feature->getDocument(); document->commitTransaction(); document->recompute(); } } void DlgProjectOnSurface::reject() { if (!feature.expired()) { auto document = feature->getDocument(); document->abortTransaction(); } } void DlgProjectOnSurface::onAddProjFaceClicked() { if (ui->pushButtonAddProjFace->isChecked()) { selectionMode = SelectionMode::SupportFace; if (!filterFace) { filterFace = new FaceSelection(); Gui::Selection().addSelectionGate(filterFace); } } else { selectionMode = SelectionMode::None; Gui::Selection().rmvSelectionGate(); filterFace = nullptr; } } void DlgProjectOnSurface::onAddFaceClicked() { if (ui->pushButtonAddFace->isChecked()) { selectionMode = SelectionMode::AddFace; if (!filterFace) { filterFace = new FaceSelection(); Gui::Selection().addSelectionGate(filterFace); } } else { selectionMode = SelectionMode::None; Gui::Selection().rmvSelectionGate(); filterFace = nullptr; } } void DlgProjectOnSurface::onAddWireClicked() { if (ui->pushButtonAddWire->isChecked()) { selectionMode = SelectionMode::AddWire; if (!filterEdge) { filterEdge = new EdgeSelection(); Gui::Selection().addSelectionGate(filterEdge); } ui->radioButtonEdges->setChecked(true); onEdgesClicked(); } else { selectionMode = SelectionMode::None; Gui::Selection().rmvSelectionGate(); filterEdge = nullptr; } } void DlgProjectOnSurface::onAddEdgeClicked() { if (ui->pushButtonAddEdge->isChecked()) { selectionMode = SelectionMode::AddEdge; if (!filterEdge) { filterEdge = new EdgeSelection(); Gui::Selection().addSelectionGate(filterEdge); } ui->radioButtonEdges->setChecked(true); onEdgesClicked(); } else { selectionMode = SelectionMode::None; Gui::Selection().rmvSelectionGate(); filterEdge = nullptr; } } void DlgProjectOnSurface::onGetCurrentCamDirClicked() { auto mainWindow = Gui::getMainWindow(); auto mdiObject = dynamic_cast(mainWindow->activeWindow()); if (!mdiObject) { return; } auto camerRotation = mdiObject->getViewer()->getCameraOrientation(); SbVec3f lookAt(0, 0, -1); camerRotation.multVec(lookAt, lookAt); float valX {}; float valY {}; float valZ {}; lookAt.getValue(valX, valY, valZ); ui->doubleSpinBoxDirX->setValue(valX); ui->doubleSpinBoxDirY->setValue(valY); ui->doubleSpinBoxDirZ->setValue(valZ); setDirection(); } void DlgProjectOnSurface::onDirXClicked() { auto currentVal = ui->doubleSpinBoxDirX->value(); ui->doubleSpinBoxDirX->setValue(currentVal > 0 ? -1 : 1); ui->doubleSpinBoxDirY->setValue(0); ui->doubleSpinBoxDirZ->setValue(0); setDirection(); } void DlgProjectOnSurface::onDirYClicked() { auto currentVal = ui->doubleSpinBoxDirY->value(); ui->doubleSpinBoxDirX->setValue(0); ui->doubleSpinBoxDirY->setValue(currentVal > 0 ? -1 : 1); ui->doubleSpinBoxDirZ->setValue(0); setDirection(); } void DlgProjectOnSurface::onDirZClicked() { auto currentVal = ui->doubleSpinBoxDirZ->value(); ui->doubleSpinBoxDirX->setValue(0); ui->doubleSpinBoxDirY->setValue(0); ui->doubleSpinBoxDirZ->setValue(currentVal > 0 ? -1 : 1); setDirection(); } void DlgProjectOnSurface::setDirection() { if (!feature.expired()) { auto xVal = ui->doubleSpinBoxDirX->value(); auto yVal = ui->doubleSpinBoxDirY->value(); auto zVal = ui->doubleSpinBoxDirZ->value(); feature->Direction.setValue(Base::Vector3d(xVal, yVal, zVal)); feature->recomputeFeature(); } } void DlgProjectOnSurface::addWire(const Gui::SelectionChanges& msg) { auto isEdgePartOf = [](const TopoDS_Shape& wire, const TopoDS_Shape& edge) { for (TopExp_Explorer xp(wire, TopAbs_EDGE); xp.More(); xp.Next()) { if (edge.IsSame(xp.Current())) { return true; } } return false; }; if (selectionMode != SelectionMode::AddWire) { return; } Gui::SelectionObject selObj(msg); if (!selObj.hasSubNames()) { return; } Part::TopoShape part = Part::Feature::getTopoShape( selObj.getObject(), Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform ); if (part.isNull()) { return; } std::string subName = selObj.getSubNames().front(); TopoDS_Shape edge = part.getSubShape(subName.c_str(), true); if (edge.IsNull() || edge.ShapeType() != TopAbs_EDGE) { return; } int index = 1; const TopoDS_Shape& shape = part.getShape(); for (TopExp_Explorer xp(shape, TopAbs_WIRE); xp.More(); xp.Next()) { if (isEdgePartOf(xp.Current(), edge)) { std::string name {"Wire"}; name += std::to_string(index); addSelection(msg, name); break; } index++; } } void DlgProjectOnSurface::addSelection(const Gui::SelectionChanges& msg, const std::string& subName) { if (!feature.expired()) { Gui::SelectionObject selObj(msg); feature->Projection.addValue(selObj.getObject(), {subName}); } } void DlgProjectOnSurface::addSelection(const Gui::SelectionChanges& msg) { if (!feature.expired()) { Gui::SelectionObject selObj(msg); feature->Projection.addValue(selObj.getObject(), selObj.getSubNames()); } } void DlgProjectOnSurface::setSupportFace(const Gui::SelectionChanges& msg) { Gui::SelectionObject selObj(msg); if (!feature.expired()) { feature->SupportFace.setValue(selObj.getObject(), selObj.getSubNames()); feature->recomputeFeature(); } } void DlgProjectOnSurface::fetchDirection() { if (!feature.expired()) { auto dir = feature->Direction.getValue(); ui->doubleSpinBoxDirX->setValue(dir.x); ui->doubleSpinBoxDirY->setValue(dir.y); ui->doubleSpinBoxDirZ->setValue(dir.z); } } void DlgProjectOnSurface::fetchMode() { if (!feature.expired()) { if (strcmp(feature->Mode.getValueAsString(), Part::ProjectOnSurface::AllMode) == 0) { ui->radioButtonShowAll->setChecked(true); } else if (strcmp(feature->Mode.getValueAsString(), Part::ProjectOnSurface::FacesMode) == 0) { ui->radioButtonFaces->setChecked(true); } else if (strcmp(feature->Mode.getValueAsString(), Part::ProjectOnSurface::EdgesMode) == 0) { ui->radioButtonEdges->setChecked(true); } } } void DlgProjectOnSurface::onShowAllClicked() { if (!feature.expired()) { feature->Mode.setValue(Part::ProjectOnSurface::AllMode); feature->recomputeFeature(); } } void DlgProjectOnSurface::onFacesClicked() { if (!feature.expired()) { feature->Mode.setValue(Part::ProjectOnSurface::FacesMode); feature->recomputeFeature(); } } void DlgProjectOnSurface::onEdgesClicked() { if (!feature.expired()) { feature->Mode.setValue(Part::ProjectOnSurface::EdgesMode); feature->recomputeFeature(); } } void DlgProjectOnSurface::onExtrudeHeightValueChanged(double value) { if (!feature.expired()) { feature->Height.setValue(value); feature->recomputeFeature(); } } void DlgProjectOnSurface::onSolidDepthValueChanged(double value) { if (!feature.expired()) { feature->Offset.setValue(value); feature->recomputeFeature(); } } // --------------------------------------- TaskProjectOnSurface::TaskProjectOnSurface(App::Document* doc) { setDocumentName(doc->getName()); doc->openTransaction(QT_TRANSLATE_NOOP("Command", "Project on surface")); auto feature = doc->addObject("Projection"); widget = new DlgProjectOnSurface(feature); taskbox = new Gui::TaskView::TaskBox( Gui::BitmapFactory().pixmap("Part_ProjectionOnSurface"), widget->windowTitle(), true, nullptr ); taskbox->groupLayout()->addWidget(widget); Content.push_back(taskbox); } TaskProjectOnSurface::TaskProjectOnSurface(Part::ProjectOnSurface* feature) : widget(new DlgProjectOnSurface(feature)) , taskbox(new Gui::TaskView::TaskBox( Gui::BitmapFactory().pixmap("Part_ProjectionOnSurface"), widget->windowTitle(), true, nullptr )) { taskbox->groupLayout()->addWidget(widget); Content.push_back(taskbox); } void TaskProjectOnSurface::resetEdit() { std::string document = getDocumentName(); Gui::doCommandT(Gui::Command::Gui, "Gui.getDocument('%s').resetEdit()", document); } bool TaskProjectOnSurface::accept() { widget->accept(); resetEdit(); return true; } bool TaskProjectOnSurface::reject() { widget->reject(); resetEdit(); return true; } #include "moc_DlgProjectionOnSurface.cpp"