diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index 6c99f8177f..5da58f1c97 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -98,6 +98,7 @@ #include "GeometryIntExtensionPy.h" #include "GeometryMigrationExtension.h" #include "GeometryStringExtensionPy.h" +#include "PreviewExtension.h" #include "HyperbolaPy.h" #include "ImportStep.h" #include "LinePy.h" @@ -435,7 +436,7 @@ PyMOD_INIT_FUNC(Part) Part::AttachExtension ::init(); Part::AttachExtensionPython ::init(); - + Part::PreviewExtension ::init(); Part::PrismExtension ::init(); Part::Feature ::init(); diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index 47bb011512..984d46e5e1 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -225,6 +225,8 @@ SET(Features_SRCS DatumFeature.h AttachExtension.h AttachExtension.cpp + PreviewExtension.cpp + PreviewExtension.h PrismExtension.cpp PrismExtension.h VectorAdapter.cpp diff --git a/src/Mod/Part/App/PreviewExtension.cpp b/src/Mod/Part/App/PreviewExtension.cpp new file mode 100644 index 0000000000..a3be90aa73 --- /dev/null +++ b/src/Mod/Part/App/PreviewExtension.cpp @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2025 Kacper Donat * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#include "PreviewExtension.h" + +#include + +EXTENSION_PROPERTY_SOURCE(Part::PreviewExtension, App::DocumentObjectExtension) +EXTENSION_PROPERTY_SOURCE_TEMPLATE(Part::PreviewExtensionPython, Part::PreviewExtension) + +Part::PreviewExtension::PreviewExtension() +{ + initExtensionType(getExtensionClassTypeId()); + + EXTENSION_ADD_PROPERTY(PreviewShape, (TopoShape())); + + PreviewShape.setStatus(App::Property::Output, true); + PreviewShape.setStatus(App::Property::Transient, true); + PreviewShape.setStatus(App::Property::Hidden, true); +} + +void Part::PreviewExtension::updatePreview() +{ + if (_isPreviewFresh) { + return; + } + + recomputePreview(); + + _isPreviewFresh = true; +} + +bool Part::PreviewExtension::mustRecomputePreview() const +{ + return getExtendedObject()->mustRecompute(); +} + +void Part::PreviewExtension::extensionOnChanged(const App::Property* prop) +{ + DocumentObjectExtension::extensionOnChanged(prop); + + if (mustRecomputePreview()) { + _isPreviewFresh = false; + } +} diff --git a/src/Mod/Part/App/PreviewExtension.h b/src/Mod/Part/App/PreviewExtension.h new file mode 100644 index 0000000000..5498e80fb9 --- /dev/null +++ b/src/Mod/Part/App/PreviewExtension.h @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2025 Kacper Donat * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + +#ifndef PART_PREVIEWEXTENSION_H +#define PART_PREVIEWEXTENSION_H + +#include "PropertyTopoShape.h" + +#include +#include +#include + +namespace Part +{ + +class PartExport PreviewExtension: public App::DocumentObjectExtension +{ + EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(Part::PreviewExtension); + using inherited = DocumentObjectExtension; + +public: + /// Shape displayed as a semi-transparent preview, should be a delta between current state and + /// previous one + PropertyPartShape PreviewShape; + + PreviewExtension(); + + bool isPreviewFresh() const + { + return _isPreviewFresh; + } + + void updatePreview(); + + virtual bool mustRecomputePreview() const; + +protected: + void extensionOnChanged(const App::Property* prop) override; + + virtual App::DocumentObjectExecReturn* recomputePreview() + { + return App::DocumentObject::StdReturn; + }; + +private: + bool _isPreviewFresh {false}; +}; + +template +class PreviewExtensionPythonT: public ExtensionT +{ + +public: + PreviewExtensionPythonT() = default; + ~PreviewExtensionPythonT() override = default; +}; + +using PreviewExtensionPython = App::ExtensionPythonT>; + +} // namespace Part + +#endif // PART_PREVIEWEXTENSION_H diff --git a/src/Mod/Part/App/Tools.cpp b/src/Mod/Part/App/Tools.cpp index 12cfe3f949..71caa4dac8 100644 --- a/src/Mod/Part/App/Tools.cpp +++ b/src/Mod/Part/App/Tools.cpp @@ -71,7 +71,11 @@ #include "Tools.h" +#include +#include + +class TopExp_Explorer; void Part::closestPointsOnLines(const gp_Lin& lin1, const gp_Lin& lin2, gp_Pnt& p1, gp_Pnt& p2) { // they might be the same point @@ -746,36 +750,71 @@ TopLoc_Location Part::Tools::fromPlacement(const Base::Placement& plm) return {trf}; } -bool Part::Tools::isConcave(const TopoDS_Face &face, const gp_Pnt &pointOfVue, const gp_Dir &direction){ +bool Part::Tools::isConcave(const TopoDS_Face& face, + const gp_Pnt& pointOfVue, + const gp_Dir& direction) +{ bool result = false; Handle(Geom_Surface) surf = BRep_Tool::Surface(face); GeomAdaptor_Surface adapt(surf); - if(adapt.GetType() == GeomAbs_Plane){ + if (adapt.GetType() == GeomAbs_Plane) { return false; } - // create a line through the point of vue + // create a line through the point of vue gp_Lin line; line.SetLocation(pointOfVue); line.SetDirection(direction); - // Find intersection of line with the face + // Find intersection of line with the face BRepIntCurveSurface_Inter mkSection; mkSection.Init(face, line, Precision::Confusion()); result = mkSection.Transition() == IntCurveSurface_In; - // compute normals at the intersection + // compute normals at the intersection gp_Pnt iPnt; gp_Vec dU, dV; surf->D1(mkSection.U(), mkSection.V(), iPnt, dU, dV); - // check normals orientation + // check normals orientation gp_Dir dirdU(dU); - result = (dirdU.Angle(direction) - std::numbers::pi/2) <= Precision::Confusion(); + result = (dirdU.Angle(direction) - std::numbers::pi / 2) <= Precision::Confusion(); gp_Dir dirdV(dV); - result = result || ((dirdV.Angle(direction) - std::numbers::pi/2) <= Precision::Confusion()); + result = result || ((dirdV.Angle(direction) - std::numbers::pi / 2) <= Precision::Confusion()); return result; } + +bool Part::Tools::isShapeEmpty(const TopoShape& shape) +{ + return shape.isEmpty(); +} + +bool Part::Tools::isShapeEmpty(const TopoDS_Shape& shape) +{ + static const auto isEveryShapeInCompoundEmpty = [](const TopoDS_Shape& shape) { + for (TopoDS_Iterator it(shape); it.More(); it.Next()) { + if (const TopoDS_Shape& sub = it.Value(); !isShapeEmpty(sub)) { + // Found a non-empty sub-shape + return false; + } + } + + return true; + }; + + // If shape is null we consider it as empty + if (shape.IsNull()) { + return true; + } + + if (shape.ShapeType() == TopAbs_COMPOUND) { + return isEveryShapeInCompoundEmpty(shape); + } + + // To see if shape is non-empty we check if it has at least one vertex + TopExp_Explorer explorer(shape, TopAbs_VERTEX); + return !explorer.More(); +} diff --git a/src/Mod/Part/App/Tools.h b/src/Mod/Part/App/Tools.h index eec971dba8..64a21e18e8 100644 --- a/src/Mod/Part/App/Tools.h +++ b/src/Mod/Part/App/Tools.h @@ -43,6 +43,10 @@ #include +namespace Part +{ +class TopoShape; +} class gp_Lin; class gp_Pln; @@ -235,6 +239,26 @@ public: * and false otherwise, plane case included. */ static bool isConcave(const TopoDS_Face &face, const gp_Pnt &pointOfVue, const gp_Dir &direction); + + /** + * \copydoc Part::Tools::isShapeEmpty(const TopoDS_Shape&) + */ + static bool isShapeEmpty(const TopoShape& shape); + + /** + * \brief Determines whether the given \ref TopoDS_Shape is empty. + * + * This function evaluates whether a given shape is considered "empty." + * + * A shape is empty if: + * - It is null (uninitialized). + * - It is a compound shape (i.e., a container for sub-shapes), but all its sub-shapes are empty. + * - It does not have any geometry. + * + * \param[in] shape The shape to evaluate. + * \return `true` if the shape is empty, otherwise `false`. + */ + static bool isShapeEmpty(const TopoDS_Shape& shape); }; } //namespace Part diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 6793e16210..28cc7198ce 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -1361,6 +1361,11 @@ bool TopoShape::isValid() const return aChecker.IsValid() ? true : false; } +bool TopoShape::isEmpty() const +{ + return Tools::isShapeEmpty(this->_Shape); +} + namespace Part { std::vector buildShapeEnumVector() { diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 92998cae3f..f788c1b532 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -487,6 +487,7 @@ public: //@{ bool isNull() const; bool isValid() const; + bool isEmpty() const; bool analyze(bool runBopCheck, std::ostream&) const; bool isClosed() const; bool isCoplanar(const TopoShape& other, double tol = -1) const; diff --git a/src/Mod/Part/Gui/AppPartGui.cpp b/src/Mod/Part/Gui/AppPartGui.cpp index 264ea28025..ce4e6c3848 100644 --- a/src/Mod/Part/Gui/AppPartGui.cpp +++ b/src/Mod/Part/Gui/AppPartGui.cpp @@ -68,6 +68,7 @@ #include "ViewProviderMirror.h" #include "ViewProviderPlaneParametric.h" #include "ViewProviderPointParametric.h" +#include "ViewProviderPreviewExtension.h" #include "ViewProviderPrism.h" #include "ViewProviderProjectOnSurface.h" #include "ViewProviderRegularPolygon.h" @@ -162,10 +163,13 @@ PyMOD_INIT_FUNC(PartGui) PartGui::SoBrepEdgeSet ::initClass(); PartGui::SoBrepPointSet ::initClass(); PartGui::SoFCControlPoints ::initClass(); + PartGui::SoPreviewShape ::initClass(); PartGui::ViewProviderAttachExtension ::init(); PartGui::ViewProviderAttachExtensionPython ::init(); PartGui::ViewProviderGridExtension ::init(); PartGui::ViewProviderGridExtensionPython ::init(); + PartGui::ViewProviderPreviewExtension ::init(); + PartGui::ViewProviderPreviewExtensionPython ::init(); PartGui::ViewProviderSplineExtension ::init(); PartGui::ViewProviderSplineExtensionPython ::init(); PartGui::ViewProviderLine ::init(); diff --git a/src/Mod/Part/Gui/CMakeLists.txt b/src/Mod/Part/Gui/CMakeLists.txt index 485ba9cecf..c9e569cf34 100644 --- a/src/Mod/Part/Gui/CMakeLists.txt +++ b/src/Mod/Part/Gui/CMakeLists.txt @@ -162,6 +162,8 @@ SET(PartGui_SRCS ViewProvider.h ViewProviderAttachExtension.h ViewProviderAttachExtension.cpp + ViewProviderPreviewExtension.h + ViewProviderPreviewExtension.cpp ViewProviderDatum.cpp ViewProviderDatum.h ViewProviderExt.cpp diff --git a/src/Mod/Part/Gui/ViewProviderExt.cpp b/src/Mod/Part/Gui/ViewProviderExt.cpp index 70c286cafb..602c13f5a8 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.cpp +++ b/src/Mod/Part/Gui/ViewProviderExt.cpp @@ -564,7 +564,7 @@ std::vector ViewProviderPartExt::getModelPoints(const SoPickedPo try { std::vector pts; std::string element = this->getElement(pp->getDetail()); - const auto &shape = Part::Feature::getTopoShape(getObject(), Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform); + const auto &shape = getRenderedShape(); TopoDS_Shape subShape = shape.getSubShape(element.c_str()); @@ -926,6 +926,404 @@ void ViewProviderPartExt::unsetEdit(int ModNum) } } +void ViewProviderPartExt::setupCoinGeometry(TopoDS_Shape shape, + SoCoordinate3* coords, + SoBrepFaceSet* faceset, + SoNormal* norm, + SoBrepEdgeSet* lineset, + SoBrepPointSet* nodeset, + double deviation, + double angularDeflection, + bool normalsFromUV) +{ + if (Part::Tools::isShapeEmpty(shape)) { + coords->point.setNum(0); + norm->vector.setNum(0); + faceset->coordIndex.setNum(0); + faceset->partIndex.setNum(0); + lineset->coordIndex.setNum(0); + nodeset->startIndex.setValue(0); + return; + } + + // time measurement and book keeping + Base::TimeElapsed startTime; + + [[maybe_unused]] + int numTriangles = 0, numNodes = 0, numNorms = 0, numFaces = 0, numEdges = 0, numLines = 0; + + std::set faceEdges; + + // calculating the deflection value + Bnd_Box bounds; + BRepBndLib::Add(shape, bounds); + bounds.SetGap(0.0); + Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; + bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); + Standard_Real deflection = ((xMax - xMin) + (yMax - yMin) + (zMax - zMin)) / 300.0 * deviation; + + // Since OCCT 7.6 a value of equal 0 is not allowed any more, this can happen if a single + // vertex should be displayed. + if (deflection < gp::Resolution()) { + deflection = Precision::Confusion(); + } + + // For very big objects the computed deflection can become very high and thus leads to a + // useless tessellation. To avoid this the upper limit is set to 20.0 See also forum: + // https://forum.freecad.org/viewtopic.php?t=77521 + // deflection = std::min(deflection, 20.0); + + // create or use the mesh on the data structure + Standard_Real AngDeflectionRads = Base::toRadians(angularDeflection); + + IMeshTools_Parameters meshParams; + meshParams.Deflection = deflection; + meshParams.Relative = Standard_False; + meshParams.Angle = AngDeflectionRads; + meshParams.InParallel = Standard_True; + meshParams.AllowQualityDecrease = Standard_True; + + BRepMesh_IncrementalMesh(shape, meshParams); + + // We must reset the location here because the transformation data + // are set in the placement property + TopLoc_Location aLoc; + shape.Location(aLoc); + + // count triangles and nodes in the mesh + TopTools_IndexedMapOfShape faceMap; + TopExp::MapShapes(shape, TopAbs_FACE, faceMap); + for (int i = 1; i <= faceMap.Extent(); i++) { + Handle(Poly_Triangulation) mesh = BRep_Tool::Triangulation(TopoDS::Face(faceMap(i)), aLoc); + + if (mesh.IsNull()) { + mesh = Part::Tools::triangulationOfFace(TopoDS::Face(faceMap(i))); + } + + // Note: we must also count empty faces + if (!mesh.IsNull()) { + numTriangles += mesh->NbTriangles(); + numNodes += mesh->NbNodes(); + numNorms += mesh->NbNodes(); + } + + TopExp_Explorer xp; + for (xp.Init(faceMap(i), TopAbs_EDGE); xp.More(); xp.Next()) { + faceEdges.insert(Part::ShapeMapHasher {}(xp.Current())); + } + numFaces++; + } + + // get an indexed map of edges + TopTools_IndexedMapOfShape edgeMap; + TopExp::MapShapes(shape, TopAbs_EDGE, edgeMap); + + // key is the edge number, value the coord indexes. This is needed to keep the same order as + // the edges. + std::map> lineSetMap; + std::set edgeIdxSet; + std::vector edgeVector; + + // count and index the edges + for (int i = 1; i <= edgeMap.Extent(); i++) { + edgeIdxSet.insert(i); + numEdges++; + + const TopoDS_Edge& aEdge = TopoDS::Edge(edgeMap(i)); + TopLoc_Location aLoc; + + // handling of the free edge that are not associated to a face + // Note: The assumption that if for an edge BRep_Tool::Polygon3D + // returns a valid object is wrong. This e.g. happens for ruled + // surfaces which gets created by two edges or wires. + // So, we have to store the hashes of the edges associated to a face. + // If the hash of a given edge is not in this list we know it's really + // a free edge. + int hash = Part::ShapeMapHasher {}(aEdge); + if (faceEdges.find(hash) == faceEdges.end()) { + Handle(Poly_Polygon3D) aPoly = Part::Tools::polygonOfEdge(aEdge, aLoc); + if (!aPoly.IsNull()) { + int nbNodesInEdge = aPoly->NbNodes(); + numNodes += nbNodesInEdge; + } + } + } + + // handling of the vertices + TopTools_IndexedMapOfShape vertexMap; + TopExp::MapShapes(shape, TopAbs_VERTEX, vertexMap); + numNodes += vertexMap.Extent(); + + // create memory for the nodes and indexes + coords->point.setNum(numNodes); + norm->vector.setNum(numNorms); + faceset->coordIndex.setNum(numTriangles * 4); + faceset->partIndex.setNum(numFaces); + + // get the raw memory for fast fill up + SbVec3f* verts = coords->point.startEditing(); + SbVec3f* norms = norm->vector.startEditing(); + int32_t* index = faceset->coordIndex.startEditing(); + int32_t* parts = faceset->partIndex.startEditing(); + + // preset the normal vector with null vector + for (int i = 0; i < numNorms; i++) { + norms[i] = SbVec3f(0.0, 0.0, 0.0); + } + + int ii = 0, faceNodeOffset = 0, faceTriaOffset = 0; + for (int i = 1; i <= faceMap.Extent(); i++, ii++) { + TopLoc_Location aLoc; + const TopoDS_Face& actFace = TopoDS::Face(faceMap(i)); + // get the mesh of the shape + Handle(Poly_Triangulation) mesh = BRep_Tool::Triangulation(actFace, aLoc); + if (mesh.IsNull()) { + mesh = Part::Tools::triangulationOfFace(actFace); + } + if (mesh.IsNull()) { + parts[ii] = 0; + continue; + } + + // getting the transformation of the shape/face + gp_Trsf myTransf; + Standard_Boolean identity = true; + if (!aLoc.IsIdentity()) { + identity = false; + myTransf = aLoc.Transformation(); + } + + // getting size of node and triangle array of this face + int nbNodesInFace = mesh->NbNodes(); + int nbTriInFace = mesh->NbTriangles(); + // check orientation + TopAbs_Orientation orient = actFace.Orientation(); + + // cycling through the poly mesh +#if OCC_VERSION_HEX < 0x070600 + const Poly_Array1OfTriangle& Triangles = mesh->Triangles(); + const TColgp_Array1OfPnt& Nodes = mesh->Nodes(); + TColgp_Array1OfDir Normals(Nodes.Lower(), Nodes.Upper()); +#else + int numNodes = mesh->NbNodes(); + TColgp_Array1OfDir Normals(1, numNodes); +#endif + if (normalsFromUV) { + Part::Tools::getPointNormals(actFace, mesh, Normals); + } + + for (int g = 1; g <= nbTriInFace; g++) { + // Get the triangle + Standard_Integer N1, N2, N3; +#if OCC_VERSION_HEX < 0x070600 + Triangles(g).Get(N1, N2, N3); +#else + mesh->Triangle(g).Get(N1, N2, N3); +#endif + + // change orientation of the triangle if the face is reversed + if (orient != TopAbs_FORWARD) { + Standard_Integer tmp = N1; + N1 = N2; + N2 = tmp; + } + + // get the 3 points of this triangle +#if OCC_VERSION_HEX < 0x070600 + gp_Pnt V1(Nodes(N1)), V2(Nodes(N2)), V3(Nodes(N3)); +#else + gp_Pnt V1(mesh->Node(N1)), V2(mesh->Node(N2)), V3(mesh->Node(N3)); +#endif + + // get the 3 normals of this triangle + gp_Vec NV1, NV2, NV3; + if (normalsFromUV) { + NV1.SetXYZ(Normals(N1).XYZ()); + NV2.SetXYZ(Normals(N2).XYZ()); + NV3.SetXYZ(Normals(N3).XYZ()); + } + else { + gp_Vec v1(V1.X(), V1.Y(), V1.Z()), + v2(V2.X(), V2.Y(), V2.Z()), + v3(V3.X(), V3.Y(), V3.Z()); + + gp_Vec normal = (v2 - v1) ^ (v3 - v1); + NV1 = normal; + NV2 = normal; + NV3 = normal; + } + + // transform the vertices and normals to the place of the face + if (!identity) { + V1.Transform(myTransf); + V2.Transform(myTransf); + V3.Transform(myTransf); + if (normalsFromUV) { + NV1.Transform(myTransf); + NV2.Transform(myTransf); + NV3.Transform(myTransf); + } + } + + // add the normals for all points of this triangle + norms[faceNodeOffset + N1 - 1] += SbVec3f(NV1.X(), NV1.Y(), NV1.Z()); + norms[faceNodeOffset + N2 - 1] += SbVec3f(NV2.X(), NV2.Y(), NV2.Z()); + norms[faceNodeOffset + N3 - 1] += SbVec3f(NV3.X(), NV3.Y(), NV3.Z()); + + // set the vertices + verts[faceNodeOffset + N1 - 1].setValue((float)(V1.X()), + (float)(V1.Y()), + (float)(V1.Z())); + verts[faceNodeOffset + N2 - 1].setValue((float)(V2.X()), + (float)(V2.Y()), + (float)(V2.Z())); + verts[faceNodeOffset + N3 - 1].setValue((float)(V3.X()), + (float)(V3.Y()), + (float)(V3.Z())); + + // set the index vector with the 3 point indexes and the end delimiter + index[faceTriaOffset * 4 + 4 * (g - 1)] = faceNodeOffset + N1 - 1; + index[faceTriaOffset * 4 + 4 * (g - 1) + 1] = faceNodeOffset + N2 - 1; + index[faceTriaOffset * 4 + 4 * (g - 1) + 2] = faceNodeOffset + N3 - 1; + index[faceTriaOffset * 4 + 4 * (g - 1) + 3] = SO_END_FACE_INDEX; + } + + parts[ii] = nbTriInFace; // new part + + // handling the edges lying on this face + TopExp_Explorer Exp; + for (Exp.Init(actFace, TopAbs_EDGE); Exp.More(); Exp.Next()) { + const TopoDS_Edge& curEdge = TopoDS::Edge(Exp.Current()); + // get the overall index of this edge + int edgeIndex = edgeMap.FindIndex(curEdge); + edgeVector.push_back((int32_t)edgeIndex - 1); + // already processed this index ? + if (edgeIdxSet.find(edgeIndex) != edgeIdxSet.end()) { + + // this holds the indices of the edge's triangulation to the current polygon + Handle(Poly_PolygonOnTriangulation) aPoly = + BRep_Tool::PolygonOnTriangulation(curEdge, mesh, aLoc); + if (aPoly.IsNull()) { + continue; // polygon does not exist + } + + // getting the indexes of the edge polygon + const TColStd_Array1OfInteger& indices = aPoly->Nodes(); + for (Standard_Integer i = indices.Lower(); i <= indices.Upper(); i++) { + int nodeIndex = indices(i); + int index = faceNodeOffset + nodeIndex - 1; + lineSetMap[edgeIndex].push_back(index); + + // usually the coordinates for this edge are already set by the + // triangles of the face this edge belongs to. However, there are + // rare cases where some points are only referenced by the polygon + // but not by any triangle. Thus, we must apply the coordinates to + // make sure that everything is properly set. +#if OCC_VERSION_HEX < 0x070600 + gp_Pnt p(Nodes(nodeIndex)); +#else + gp_Pnt p(mesh->Node(nodeIndex)); +#endif + if (!identity) { + p.Transform(myTransf); + } + verts[index].setValue((float)(p.X()), (float)(p.Y()), (float)(p.Z())); + } + + // remove the handled edge index from the set + edgeIdxSet.erase(edgeIndex); + } + } + + edgeVector.push_back(-1); + + // counting up the per Face offsets + faceNodeOffset += nbNodesInFace; + faceTriaOffset += nbTriInFace; + } + + // handling of the free edges + for (int i = 1; i <= edgeMap.Extent(); i++) { + const TopoDS_Edge& aEdge = TopoDS::Edge(edgeMap(i)); + Standard_Boolean identity = true; + gp_Trsf myTransf; + TopLoc_Location aLoc; + + // handling of the free edge that are not associated to a face + int hash = Part::ShapeMapHasher {}(aEdge); + if (faceEdges.find(hash) == faceEdges.end()) { + Handle(Poly_Polygon3D) aPoly = Part::Tools::polygonOfEdge(aEdge, aLoc); + if (!aPoly.IsNull()) { + if (!aLoc.IsIdentity()) { + identity = false; + myTransf = aLoc.Transformation(); + } + + const TColgp_Array1OfPnt& aNodes = aPoly->Nodes(); + int nbNodesInEdge = aPoly->NbNodes(); + + gp_Pnt pnt; + for (Standard_Integer j = 1; j <= nbNodesInEdge; j++) { + pnt = aNodes(j); + if (!identity) { + pnt.Transform(myTransf); + } + int index = faceNodeOffset + j - 1; + verts[index].setValue((float)(pnt.X()), (float)(pnt.Y()), (float)(pnt.Z())); + lineSetMap[i].push_back(index); + } + + faceNodeOffset += nbNodesInEdge; + } + } + } + + nodeset->startIndex.setValue(faceNodeOffset); + for (int i = 0; i < vertexMap.Extent(); i++) { + const TopoDS_Vertex& aVertex = TopoDS::Vertex(vertexMap(i + 1)); + gp_Pnt pnt = BRep_Tool::Pnt(aVertex); + verts[faceNodeOffset + i].setValue((float)(pnt.X()), + (float)(pnt.Y()), + (float)(pnt.Z())); + } + + // normalize all normals + for (int i = 0; i < numNorms; i++) { + norms[i].normalize(); + } + + std::vector lineSetCoords; + for (const auto& it : lineSetMap) { + lineSetCoords.insert(lineSetCoords.end(), it.second.begin(), it.second.end()); + lineSetCoords.push_back(-1); + } + + // preset the index vector size + numLines = lineSetCoords.size(); + lineset->coordIndex.setNum(numLines); + int32_t* lines = lineset->coordIndex.startEditing(); + + int l = 0; + for (std::vector::const_iterator it = lineSetCoords.begin(); + it != lineSetCoords.end(); + ++it, l++) { + lines[l] = *it; + } + + // end the editing of the nodes + coords->point.finishEditing(); + norm->vector.finishEditing(); + faceset->coordIndex.finishEditing(); + faceset->partIndex.finishEditing(); + lineset->coordIndex.finishEditing(); + +# ifdef FC_DEBUG + // printing some information + Base::Console().log("ViewProvider update time: %f s\n",Base::TimeElapsed::diffTimeF(startTime,Base::TimeElapsed())); + Base::Console().log("Shape tria info: Faces:%d Edges:%d Nodes:%d Triangles:%d IdxVec:%d\n",numFaces,numEdges,numNodes,numTriangles,numLines); +# endif +} + void ViewProviderPartExt::updateVisual() { Gui::SoUpdateVBOAction action; @@ -943,382 +1341,30 @@ void ViewProviderPartExt::updateVisual() haction.apply(this->lineset); haction.apply(this->nodeset); - TopoDS_Shape cShape = Part::Feature::getShape(getObject(), Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform); - if (cShape.IsNull()) { - coords ->point .setNum(0); - norm ->vector .setNum(0); - faceset ->coordIndex .setNum(0); - faceset ->partIndex .setNum(0); - lineset ->coordIndex .setNum(0); - nodeset ->startIndex .setValue(0); - VisualTouched = false; - return; - } - - // time measurement and book keeping - Base::TimeElapsed start_time; - int numTriangles=0,numNodes=0,numNorms=0,numFaces=0,numEdges=0,numLines=0; - std::set faceEdges; - try { - // calculating the deflection value - Bnd_Box bounds; - BRepBndLib::Add(cShape, bounds); - bounds.SetGap(0.0); - Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; - bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); - Standard_Real deflection = ((xMax-xMin)+(yMax-yMin)+(zMax-zMin))/300.0 * Deviation.getValue(); + TopoDS_Shape cShape = getRenderedShape().getShape(); - // Since OCCT 7.6 a value of equal 0 is not allowed any more, this can happen if a single vertex - // should be displayed. - if (deflection < gp::Resolution()) { - deflection = Precision::Confusion(); - } + setupCoinGeometry(cShape, + coords, + faceset, + norm, + lineset, + nodeset, + Deviation.getValue(), + AngularDeflection.getValue(), + NormalsFromUV); - // For very big objects the computed deflection can become very high and thus leads to a useless - // tessellation. To avoid this the upper limit is set to 20.0 - // See also forum: https://forum.freecad.org/viewtopic.php?t=77521 - //deflection = std::min(deflection, 20.0); - - // create or use the mesh on the data structure - Standard_Real AngDeflectionRads = Base::toRadians(AngularDeflection.getValue()); - - IMeshTools_Parameters meshParams; - meshParams.Deflection = deflection; - meshParams.Relative = Standard_False; - meshParams.Angle = AngDeflectionRads; - meshParams.InParallel = Standard_True; - meshParams.AllowQualityDecrease = Standard_True; - - BRepMesh_IncrementalMesh(cShape, meshParams); - - // We must reset the location here because the transformation data - // are set in the placement property - TopLoc_Location aLoc; - cShape.Location(aLoc); - - // count triangles and nodes in the mesh - TopTools_IndexedMapOfShape faceMap; - TopExp::MapShapes(cShape, TopAbs_FACE, faceMap); - for (int i=1; i <= faceMap.Extent(); i++) { - Handle (Poly_Triangulation) mesh = BRep_Tool::Triangulation(TopoDS::Face(faceMap(i)), aLoc); - if (mesh.IsNull()) { - mesh = Part::Tools::triangulationOfFace(TopoDS::Face(faceMap(i))); - } - // Note: we must also count empty faces - if (!mesh.IsNull()) { - numTriangles += mesh->NbTriangles(); - numNodes += mesh->NbNodes(); - numNorms += mesh->NbNodes(); - } - - TopExp_Explorer xp; - for (xp.Init(faceMap(i),TopAbs_EDGE);xp.More();xp.Next()) { - faceEdges.insert(Part::ShapeMapHasher{}(xp.Current())); - } - numFaces++; - } - - // get an indexed map of edges - TopTools_IndexedMapOfShape edgeMap; - TopExp::MapShapes(cShape, TopAbs_EDGE, edgeMap); - - // key is the edge number, value the coord indexes. This is needed to keep the same order as the edges. - std::map > lineSetMap; - std::set edgeIdxSet; - std::vector edgeVector; - - // count and index the edges - for (int i=1; i <= edgeMap.Extent(); i++) { - edgeIdxSet.insert(i); - numEdges++; - - const TopoDS_Edge& aEdge = TopoDS::Edge(edgeMap(i)); - TopLoc_Location aLoc; - - // handling of the free edge that are not associated to a face - // Note: The assumption that if for an edge BRep_Tool::Polygon3D - // returns a valid object is wrong. This e.g. happens for ruled - // surfaces which gets created by two edges or wires. - // So, we have to store the hashes of the edges associated to a face. - // If the hash of a given edge is not in this list we know it's really - // a free edge. - int hash = Part::ShapeMapHasher{}(aEdge); - if (faceEdges.find(hash) == faceEdges.end()) { - Handle(Poly_Polygon3D) aPoly = Part::Tools::polygonOfEdge(aEdge, aLoc); - if (!aPoly.IsNull()) { - int nbNodesInEdge = aPoly->NbNodes(); - numNodes += nbNodesInEdge; - } - } - } - - // handling of the vertices - TopTools_IndexedMapOfShape vertexMap; - TopExp::MapShapes(cShape, TopAbs_VERTEX, vertexMap); - numNodes += vertexMap.Extent(); - - // create memory for the nodes and indexes - coords ->point .setNum(numNodes); - norm ->vector .setNum(numNorms); - faceset ->coordIndex .setNum(numTriangles*4); - faceset ->partIndex .setNum(numFaces); - // get the raw memory for fast fill up - SbVec3f* verts = coords ->point .startEditing(); - SbVec3f* norms = norm ->vector .startEditing(); - int32_t* index = faceset ->coordIndex .startEditing(); - int32_t* parts = faceset ->partIndex .startEditing(); - - // preset the normal vector with null vector - for (int i=0;i < numNorms;i++) - norms[i]= SbVec3f(0.0,0.0,0.0); - - int ii = 0,faceNodeOffset=0,faceTriaOffset=0; - for (int i=1; i <= faceMap.Extent(); i++, ii++) { - TopLoc_Location aLoc; - const TopoDS_Face &actFace = TopoDS::Face(faceMap(i)); - // get the mesh of the shape - Handle (Poly_Triangulation) mesh = BRep_Tool::Triangulation(actFace,aLoc); - if (mesh.IsNull()) { - mesh = Part::Tools::triangulationOfFace(actFace); - } - if (mesh.IsNull()) { - parts[ii] = 0; - continue; - } - - // getting the transformation of the shape/face - gp_Trsf myTransf; - Standard_Boolean identity = true; - if (!aLoc.IsIdentity()) { - identity = false; - myTransf = aLoc.Transformation(); - } - - // getting size of node and triangle array of this face - int nbNodesInFace = mesh->NbNodes(); - int nbTriInFace = mesh->NbTriangles(); - // check orientation - TopAbs_Orientation orient = actFace.Orientation(); - - - // cycling through the poly mesh -#if OCC_VERSION_HEX < 0x070600 - const Poly_Array1OfTriangle& Triangles = mesh->Triangles(); - const TColgp_Array1OfPnt& Nodes = mesh->Nodes(); - TColgp_Array1OfDir Normals (Nodes.Lower(), Nodes.Upper()); -#else - int numNodes = mesh->NbNodes(); - TColgp_Array1OfDir Normals (1, numNodes); -#endif - if (NormalsFromUV) - Part::Tools::getPointNormals(actFace, mesh, Normals); - - for (int g=1;g<=nbTriInFace;g++) { - // Get the triangle - Standard_Integer N1,N2,N3; -#if OCC_VERSION_HEX < 0x070600 - Triangles(g).Get(N1,N2,N3); -#else - mesh->Triangle(g).Get(N1,N2,N3); -#endif - - // change orientation of the triangle if the face is reversed - if ( orient != TopAbs_FORWARD ) { - Standard_Integer tmp = N1; - N1 = N2; - N2 = tmp; - } - - // get the 3 points of this triangle -#if OCC_VERSION_HEX < 0x070600 - gp_Pnt V1(Nodes(N1)), V2(Nodes(N2)), V3(Nodes(N3)); -#else - gp_Pnt V1(mesh->Node(N1)), V2(mesh->Node(N2)), V3(mesh->Node(N3)); -#endif - - // get the 3 normals of this triangle - gp_Vec NV1, NV2, NV3; - if (NormalsFromUV) { - NV1.SetXYZ(Normals(N1).XYZ()); - NV2.SetXYZ(Normals(N2).XYZ()); - NV3.SetXYZ(Normals(N3).XYZ()); - } - else { - gp_Vec v1(V1.X(),V1.Y(),V1.Z()), - v2(V2.X(),V2.Y(),V2.Z()), - v3(V3.X(),V3.Y(),V3.Z()); - gp_Vec normal = (v2-v1)^(v3-v1); - NV1 = normal; - NV2 = normal; - NV3 = normal; - } - - // transform the vertices and normals to the place of the face - if (!identity) { - V1.Transform(myTransf); - V2.Transform(myTransf); - V3.Transform(myTransf); - if (NormalsFromUV) { - NV1.Transform(myTransf); - NV2.Transform(myTransf); - NV3.Transform(myTransf); - } - } - - // add the normals for all points of this triangle - norms[faceNodeOffset+N1-1] += SbVec3f(NV1.X(),NV1.Y(),NV1.Z()); - norms[faceNodeOffset+N2-1] += SbVec3f(NV2.X(),NV2.Y(),NV2.Z()); - norms[faceNodeOffset+N3-1] += SbVec3f(NV3.X(),NV3.Y(),NV3.Z()); - - // set the vertices - verts[faceNodeOffset+N1-1].setValue((float)(V1.X()),(float)(V1.Y()),(float)(V1.Z())); - verts[faceNodeOffset+N2-1].setValue((float)(V2.X()),(float)(V2.Y()),(float)(V2.Z())); - verts[faceNodeOffset+N3-1].setValue((float)(V3.X()),(float)(V3.Y()),(float)(V3.Z())); - - // set the index vector with the 3 point indexes and the end delimiter - index[faceTriaOffset*4+4*(g-1)] = faceNodeOffset+N1-1; - index[faceTriaOffset*4+4*(g-1)+1] = faceNodeOffset+N2-1; - index[faceTriaOffset*4+4*(g-1)+2] = faceNodeOffset+N3-1; - index[faceTriaOffset*4+4*(g-1)+3] = SO_END_FACE_INDEX; - } - - parts[ii] = nbTriInFace; // new part - - // handling the edges lying on this face - TopExp_Explorer Exp; - for(Exp.Init(actFace,TopAbs_EDGE);Exp.More();Exp.Next()) { - const TopoDS_Edge &curEdge = TopoDS::Edge(Exp.Current()); - // get the overall index of this edge - int edgeIndex = edgeMap.FindIndex(curEdge); - edgeVector.push_back((int32_t)edgeIndex-1); - // already processed this index ? - if (edgeIdxSet.find(edgeIndex)!=edgeIdxSet.end()) { - - // this holds the indices of the edge's triangulation to the current polygon - Handle(Poly_PolygonOnTriangulation) aPoly = BRep_Tool::PolygonOnTriangulation(curEdge, mesh, aLoc); - if (aPoly.IsNull()) - continue; // polygon does not exist - - // getting the indexes of the edge polygon - const TColStd_Array1OfInteger& indices = aPoly->Nodes(); - for (Standard_Integer i=indices.Lower();i <= indices.Upper();i++) { - int nodeIndex = indices(i); - int index = faceNodeOffset+nodeIndex-1; - lineSetMap[edgeIndex].push_back(index); - - // usually the coordinates for this edge are already set by the - // triangles of the face this edge belongs to. However, there are - // rare cases where some points are only referenced by the polygon - // but not by any triangle. Thus, we must apply the coordinates to - // make sure that everything is properly set. -#if OCC_VERSION_HEX < 0x070600 - gp_Pnt p(Nodes(nodeIndex)); -#else - gp_Pnt p(mesh->Node(nodeIndex)); -#endif - if (!identity) - p.Transform(myTransf); - verts[index].setValue((float)(p.X()),(float)(p.Y()),(float)(p.Z())); - } - - // remove the handled edge index from the set - edgeIdxSet.erase(edgeIndex); - } - } - - edgeVector.push_back(-1); - - // counting up the per Face offsets - faceNodeOffset += nbNodesInFace; - faceTriaOffset += nbTriInFace; - } - - // handling of the free edges - for (int i=1; i <= edgeMap.Extent(); i++) { - const TopoDS_Edge& aEdge = TopoDS::Edge(edgeMap(i)); - Standard_Boolean identity = true; - gp_Trsf myTransf; - TopLoc_Location aLoc; - - // handling of the free edge that are not associated to a face - int hash = Part::ShapeMapHasher{}(aEdge); - if (faceEdges.find(hash) == faceEdges.end()) { - Handle(Poly_Polygon3D) aPoly = Part::Tools::polygonOfEdge(aEdge, aLoc); - if (!aPoly.IsNull()) { - if (!aLoc.IsIdentity()) { - identity = false; - myTransf = aLoc.Transformation(); - } - - const TColgp_Array1OfPnt& aNodes = aPoly->Nodes(); - int nbNodesInEdge = aPoly->NbNodes(); - - gp_Pnt pnt; - for (Standard_Integer j=1;j <= nbNodesInEdge;j++) { - pnt = aNodes(j); - if (!identity) - pnt.Transform(myTransf); - int index = faceNodeOffset+j-1; - verts[index].setValue((float)(pnt.X()),(float)(pnt.Y()),(float)(pnt.Z())); - lineSetMap[i].push_back(index); - } - - faceNodeOffset += nbNodesInEdge; - } - } - } - - nodeset->startIndex.setValue(faceNodeOffset); - for (int i=0; i lineSetCoords; - for (const auto & it : lineSetMap) { - lineSetCoords.insert(lineSetCoords.end(), it.second.begin(), it.second.end()); - lineSetCoords.push_back(-1); - } - - // preset the index vector size - numLines = lineSetCoords.size(); - lineset ->coordIndex .setNum(numLines); - int32_t* lines = lineset ->coordIndex .startEditing(); - - int l=0; - for (std::vector::const_iterator it=lineSetCoords.begin();it!=lineSetCoords.end();++it,l++) - lines[l] = *it; - - // end the editing of the nodes - coords ->point .finishEditing(); - norm ->vector .finishEditing(); - faceset ->coordIndex .finishEditing(); - faceset ->partIndex .finishEditing(); - lineset ->coordIndex .finishEditing(); + VisualTouched = false; } catch (const Standard_Failure& e) { FC_ERR("Cannot compute Inventor representation for the shape of " << pcObject->getFullName() << ": " << e.GetMessageString()); } catch (...) { - FC_ERR("Cannot compute Inventor representation for the shape of " << pcObject->getFullName()); + FC_ERR("Cannot compute Inventor representation for the shape of " + << pcObject->getFullName()); } -# ifdef FC_DEBUG - // printing some information - Base::Console().log("ViewProvider update time: %f s\n",Base::TimeElapsed::diffTimeF(start_time,Base::TimeElapsed())); - Base::Console().log("Shape tria info: Faces:%d Edges:%d Nodes:%d Triangles:%d IdxVec:%d\n",numFaces,numEdges,numNodes,numTriangles,numLines); -# else - (void)numEdges; -# endif - VisualTouched = false; - // The material has to be checked again setHighlightedFaces(ShapeAppearance.getValues()); setHighlightedEdges(LineColorArray.getValues()); diff --git a/src/Mod/Part/Gui/ViewProviderExt.h b/src/Mod/Part/Gui/ViewProviderExt.h index 43f26ea84f..9c1391fdd3 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.h +++ b/src/Mod/Part/Gui/ViewProviderExt.h @@ -123,6 +123,10 @@ public: std::vector getSelectionShape(const char* Element) const override; //@} + virtual Part::TopoShape getRenderedShape() const { + return Part::Feature::getTopoShape(getObject(), Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform); + } + /** @name Highlight handling * This group of methods do the highlighting of elements. */ @@ -156,6 +160,17 @@ public: /// Get the python wrapper for that ViewProvider PyObject* getPyObject() override; + /// configures Coin nodes so they render given toposhape + static void setupCoinGeometry(TopoDS_Shape shape, + SoCoordinate3* coords, + SoBrepFaceSet* faceset, + SoNormal* norm, + SoBrepEdgeSet* lineset, + SoBrepPointSet* nodeset, + double deviation, + double angularDeflection, + bool normalsFromUV); + protected: bool setEdit(int ModNum) override; void unsetEdit(int ModNum) override; diff --git a/src/Mod/Part/Gui/ViewProviderPreviewExtension.cpp b/src/Mod/Part/Gui/ViewProviderPreviewExtension.cpp new file mode 100644 index 0000000000..a453afce77 --- /dev/null +++ b/src/Mod/Part/Gui/ViewProviderPreviewExtension.cpp @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2025 Kacper Donat * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +# include +# include +# include +#endif + +#include "ViewProviderPreviewExtension.h" +#include "ViewProviderExt.h" + +#include +#include +#include + +using namespace PartGui; + +SO_NODE_SOURCE(SoPreviewShape); + +const SbColor SoPreviewShape::defaultColor = SbColor(1.F, 0.F, 1.F); + +SoPreviewShape::SoPreviewShape() + : coords(new SoCoordinate3) + , norm(new SoNormal) + , faceset(new PartGui::SoBrepFaceSet) + , lineset(new PartGui::SoBrepEdgeSet) + , nodeset(new PartGui::SoBrepPointSet) +{ + SO_NODE_CONSTRUCTOR(SoPreviewShape); + + SO_NODE_ADD_FIELD(color, (defaultColor)); + SO_NODE_ADD_FIELD(transparency, (defaultTransparency)); + SO_NODE_ADD_FIELD(lineWidth, (defaultLineWidth)); + + auto pickStyle = new SoPickStyle; + pickStyle->style = SoPickStyle::UNPICKABLE; + + auto* solidLineStyle = new SoDrawStyle(); + solidLineStyle->lineWidth.connectFrom(&lineWidth); + + auto* hiddenLineStyle = new SoDrawStyle(); + hiddenLineStyle->lineWidth.connectFrom(&lineWidth); + hiddenLineStyle->linePattern = 0xF0F0; + + auto* solidColorLightModel = new SoLightModel(); + solidColorLightModel->model = SoLightModel::BASE_COLOR; + + auto* normalBinding = new SoNormalBinding(); + normalBinding->value = SoNormalBinding::PER_VERTEX_INDEXED; + + // This should be OVERALL but then line pattern does not work correctly + // Probably a bug in coin to be investigated. + auto* materialBinding = new SoMaterialBinding(); + materialBinding->value = SoMaterialBinding::PER_FACE_INDEXED; + + auto* material = new SoMaterial; + material->diffuseColor.connectFrom(&color); + material->transparency.connectFrom(&transparency); + + auto* polygonOffset = new SoPolygonOffset; + polygonOffset->factor = -0.00001F; + polygonOffset->units = -1.0F; + polygonOffset->on = true; + polygonOffset->styles = SoPolygonOffset::FILLED; + + auto* lineMaterial = new SoMaterial; + lineMaterial->diffuseColor.connectFrom(&color); + lineMaterial->transparency = 0.0f; + + auto* lineSep = new SoSeparator; + lineSep->addChild(normalBinding); + lineSep->addChild(materialBinding); + lineSep->addChild(solidColorLightModel); + lineSep->addChild(lineMaterial); + lineSep->addChild(lineset); + + auto* annotation = new Gui::So3DAnnotation; + annotation->addChild(hiddenLineStyle); + annotation->addChild(material); + annotation->addChild(lineSep); + annotation->addChild(polygonOffset); + annotation->addChild(faceset); + + SoSeparator::addChild(pickStyle); + SoSeparator::addChild(solidLineStyle); + SoSeparator::addChild(material); + SoSeparator::addChild(coords); + SoSeparator::addChild(norm); + SoSeparator::addChild(lineSep); + SoSeparator::addChild(polygonOffset); + SoSeparator::addChild(faceset); + SoSeparator::addChild(annotation); +} + +EXTENSION_PROPERTY_SOURCE(PartGui::ViewProviderPreviewExtension, Gui::ViewProviderExtension) + +ViewProviderPreviewExtension::ViewProviderPreviewExtension() +{ + const Base::Color magenta(1.0F, 0.0F, 1.0F); + + EXTENSION_ADD_PROPERTY_TYPE( + PreviewColor, + (magenta), + "Preview", + static_cast(App::Prop_Transient | App::Prop_Hidden), + "Color used for 3D Preview"); + + initExtensionType(ViewProviderPreviewExtension::getExtensionClassTypeId()); +} + +void ViewProviderPreviewExtension::extensionAttach(App::DocumentObject* documentObject) +{ + ViewProviderExtension::extensionAttach(documentObject); + + pcPreviewShape = new SoPreviewShape; + + pcPreviewRoot = new SoSeparator; + pcPreviewRoot->addChild(pcPreviewShape); + + updatePreviewShape(); +} +void ViewProviderPreviewExtension::extensionOnChanged(const App::Property* prop) +{ + if (prop == &PreviewColor) { + pcPreviewShape->color.setValue(Base::convertTo(PreviewColor.getValue())); + } + + ViewProviderExtension::extensionOnChanged(prop); +} + +void ViewProviderPreviewExtension::updatePreviewShape() +{ + auto vp = freecad_cast(getExtendedViewProvider()); + + if (!vp) { + return; + } + + ViewProviderPartExt::setupCoinGeometry(getPreviewShape().getShape(), + pcPreviewShape->coords, + pcPreviewShape->faceset, + pcPreviewShape->norm, + pcPreviewShape->lineset, + pcPreviewShape->nodeset, + vp->Deviation.getValue(), + vp->AngularDeflection.getValue(), + false); + + // For some reason line patterns are not rendered correctly if material binding is set to + // anything other than PER_FACE. PER_FACE material binding seems to require materialIndex per + // each distinct edge. Until that is fixed, this code forces each edge to use the first material. + unsigned lineCoordsCount = pcPreviewShape->lineset->coordIndex.getNum(); + unsigned lineCount = 1; + + for (unsigned i = 0; i < lineCoordsCount; ++i) { + if (pcPreviewShape->lineset->coordIndex[i] < 0) { + lineCount++; + } + } + + pcPreviewShape->lineset->materialIndex.setNum(lineCount); + for (unsigned i = 0; i < lineCount; ++i) { + pcPreviewShape->lineset->materialIndex.set1Value(i, 0); + } +} + +namespace Gui { +EXTENSION_PROPERTY_SOURCE_TEMPLATE(PartGui::ViewProviderPreviewExtensionPython, PartGui::ViewProviderPreviewExtension) + +// explicit template instantiation +template class PartGuiExport ViewProviderExtensionPythonT; +} diff --git a/src/Mod/Part/Gui/ViewProviderPreviewExtension.h b/src/Mod/Part/Gui/ViewProviderPreviewExtension.h new file mode 100644 index 0000000000..4213e87e45 --- /dev/null +++ b/src/Mod/Part/Gui/ViewProviderPreviewExtension.h @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2025 Kacper Donat * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + +#ifndef PARTGUI_VIEWPROVIDERPREVIEWEXTENSION_H +#define PARTGUI_VIEWPROVIDERPREVIEWEXTENSION_H + +#include "SoBrepEdgeSet.h" +#include "SoBrepFaceSet.h" +#include "SoBrepPointSet.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace PartGui { + +class PartGuiExport SoPreviewShape : public SoSeparator { + using inherited = SoSeparator; + SO_NODE_HEADER(SoPreviewShape); + +public: + static constexpr float defaultTransparency = 0.8F; + static constexpr float defaultLineWidth = 2.0F; + static const SbColor defaultColor; + + SoPreviewShape(); + + SoSFColor color; + SoSFFloat transparency; + SoSFFloat lineWidth; + + SoCoordinate3* coords; + SoNormal* norm; + + SoBrepFaceSet* faceset; + SoBrepEdgeSet* lineset; + SoBrepPointSet* nodeset; +}; + +class PartGuiExport ViewProviderPreviewExtension : public Gui::ViewProviderExtension { + Q_DECLARE_TR_FUNCTIONS(PartDesignGui::ViewProviderPreviewExtension) + EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(Gui::ViewProviderPreviewExtension); + +public: + App::PropertyColor PreviewColor; + + ViewProviderPreviewExtension(); + + /// Returns shape that should be used as the preview + virtual Part::TopoShape getPreviewShape() const { return Part::TopoShape(); }; + + void extensionAttach(App::DocumentObject*) override; + void extensionBeforeDelete() override; + + /// Returns whatever preview is enabled or not + bool isPreviewEnabled() const { return _isPreviewEnabled; } + /// Switches preview on or off + virtual void showPreview(bool enable); + +protected: + void extensionOnChanged(const App::Property* prop) override; + + /// updates geometry of the preview shape + void updatePreviewShape(); + + Gui::CoinPtr pcPreviewRoot; + Gui::CoinPtr pcPreviewShape; + +private: + bool _isPreviewEnabled {false}; +}; + +using ViewProviderPreviewExtensionPython = Gui::ViewProviderExtensionPythonT; + +} + + +#endif // PARTGUI_VIEWPROVIDERPREVIEWEXTENSION_H diff --git a/src/Mod/PartDesign/App/Feature.cpp b/src/Mod/PartDesign/App/Feature.cpp index 62283ed5bd..ccab83071e 100644 --- a/src/Mod/PartDesign/App/Feature.cpp +++ b/src/Mod/PartDesign/App/Feature.cpp @@ -44,6 +44,8 @@ #include "Body.h" #include "ShapeBinder.h" +#include + FC_LOG_LEVEL_INIT("PartDesign", true, true) @@ -70,6 +72,7 @@ Feature::Feature() BaseFeature.setStatus(App::Property::Hidden, true); App::SuppressibleExtension::initExtension(this); + Part::PreviewExtension::initExtension(this); } App::DocumentObjectExecReturn* Feature::recompute() @@ -103,9 +106,17 @@ App::DocumentObjectExecReturn* Feature::recompute() if (!failed) { updateSuppressedShape(); } + return App::DocumentObject::StdReturn; } +App::DocumentObjectExecReturn* Feature::recomputePreview() +{ + updatePreviewShape(); + + return StdReturn; +} + void Feature::setMaterialToBodyMaterial() { auto body = getFeatureBody(); @@ -172,7 +183,8 @@ void Feature::onChanged(const App::Property *prop) { if (!this->isRestoring() && this->getDocument() - && !this->getDocument()->isPerformingTransaction()) { + && !this->getDocument()->isPerformingTransaction() + ) { if (prop == &Visibility || prop == &BaseFeature) { auto body = Body::findBodyOf(this); if (body) { @@ -343,6 +355,51 @@ Part::TopoShape Feature::getBaseTopoShape(bool silent) const return result; } +void Feature::getGeneratedShapes(std::vector& faces, + std::vector& edges, + std::vector& vertices) const +{ + static const auto addAllSubShapesToSet = []( + const Part::TopoShape& shape, + const Part::TopoShape& face, + TopAbs_ShapeEnum type, + std::set& set + ) { + for (auto &subShape : face.getSubShapes(type)) { + if (int subShapeId = shape.findShape(subShape); subShapeId > 0) { + set.insert(subShapeId); + } + } + }; + + Part::TopoShape shape = Shape.getShape(); + + std::set edgeSet; + std::set vertexSet; + + int count = shape.countSubShapes(TopAbs_FACE); + + for (int faceId = 1; faceId <= count; ++faceId) { + if (Data::MappedName mapped = shape.getMappedName(Data::IndexedName::fromConst("Face", faceId)); + shape.isElementGenerated(mapped)) { + faces.push_back(faceId); + + Part::TopoShape face = shape.getSubTopoShape(TopAbs_FACE, faceId); + + addAllSubShapesToSet(shape, face, TopAbs_EDGE, edgeSet); + addAllSubShapesToSet(shape, face, TopAbs_VERTEX, vertexSet); + } + } + + std::ranges::copy(edgeSet, std::back_inserter(edges)); + std::ranges::copy(vertexSet, std::back_inserter(vertices)); +} + +void Feature::updatePreviewShape() +{ + // no-op +} + PyObject* Feature::getPyObject() { if (PythonObject.is(Py::_None())){ diff --git a/src/Mod/PartDesign/App/Feature.h b/src/Mod/PartDesign/App/Feature.h index c85524e809..995ed4a3ff 100644 --- a/src/Mod/PartDesign/App/Feature.h +++ b/src/Mod/PartDesign/App/Feature.h @@ -27,6 +27,7 @@ #include #include #include +#include #include class gp_Pnt; @@ -45,7 +46,7 @@ class Body; * Base class of all PartDesign features. * This kind of features only produce solids or fail. */ -class PartDesignExport Feature : public Part::Feature, public App::SuppressibleExtension +class PartDesignExport Feature : public Part::Feature, public App::SuppressibleExtension, public Part::PreviewExtension { PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::Feature); @@ -62,7 +63,9 @@ public: Part::PropertyPartShape SuppressedShape; /// Keep a copy of the placement before suppression to restore it back when unsuppressed, fix #20205 Base::Placement SuppressedPlacement; + App::DocumentObjectExecReturn* recompute() override; + App::DocumentObjectExecReturn* recomputePreview() override; short mustExecute() const override; @@ -84,6 +87,13 @@ public: /// Returns the BaseFeature property's TopoShape (if any) Part::TopoShape getBaseTopoShape(bool silent=false) const; + // Fills up information about which sub-shapes were generated by the feature + virtual void getGeneratedShapes(std::vector& faces, + std::vector& edges, + std::vector& vertices) const; + + virtual void updatePreviewShape(); + PyObject* getPyObject() override; const char* getViewProviderName() const override { diff --git a/src/Mod/PartDesign/App/FeatureAddSub.cpp b/src/Mod/PartDesign/App/FeatureAddSub.cpp index 4c09474ac2..2094160a92 100644 --- a/src/Mod/PartDesign/App/FeatureAddSub.cpp +++ b/src/Mod/PartDesign/App/FeatureAddSub.cpp @@ -28,10 +28,13 @@ #include #include +#include #include "FeatureAddSub.h" #include "FeaturePy.h" +#include + using namespace PartDesign; @@ -43,7 +46,12 @@ PROPERTY_SOURCE(PartDesign::FeatureAddSub, PartDesign::FeatureRefine) FeatureAddSub::FeatureAddSub() { - ADD_PROPERTY(AddSubShape,(TopoDS_Shape())); + ADD_PROPERTY(AddSubShape, (TopoDS_Shape())); +} + +void FeatureAddSub::onChanged(const App::Property* property) +{ + Feature::onChanged(property); } FeatureAddSub::Type FeatureAddSub::getAddSubType() @@ -58,16 +66,53 @@ short FeatureAddSub::mustExecute() const return PartDesign::Feature::mustExecute(); } -void FeatureAddSub::getAddSubShape(Part::TopoShape &addShape, Part::TopoShape &subShape) +void FeatureAddSub::getAddSubShape(Part::TopoShape& addShape, Part::TopoShape& subShape) { - if (addSubType == Additive) + if (addSubType == Additive) { addShape = AddSubShape.getShape(); - else if (addSubType == Subtractive) + } + else if (addSubType == Subtractive) { subShape = AddSubShape.getShape(); + } } +void FeatureAddSub::updatePreviewShape() +{ + const auto notifyWarning = [](const QString& message) { + Base::Console().translatedUserWarning( + "Preview", + tr("Failure while computing removed volume preview: %1") + .arg(message) + .toUtf8()); + }; + + // for subtractive shapes we want to also showcase removed volume, not only the tool + if (addSubType == Subtractive) { + TopoShape base = getBaseTopoShape(true).moved(getLocation().Inverted()); + + if (const TopoShape& addSubShape = AddSubShape.getShape(); !addSubShape.isEmpty()) { + try { + base.makeElementBoolean(Part::OpCodes::Common, { base, addSubShape }); + } catch (Standard_Failure& e) { + notifyWarning(QString::fromUtf8(e.GetMessageString())); + } catch (Base::Exception& e) { + notifyWarning(QString::fromStdString(e.getMessage())); + } + + if (base.isEmpty()) { + notifyWarning(tr("Resulting shape is empty. That may indicate that no material will be removed or a problem with the model.")); + } + + PreviewShape.setValue(base); + return; + } + } + + PreviewShape.setValue(AddSubShape.getShape()); } +} // namespace PartDesign + namespace App { /// @cond DOXERR PROPERTY_SOURCE_TEMPLATE(PartDesign::FeatureAddSubPython, PartDesign::FeatureAddSub) diff --git a/src/Mod/PartDesign/App/FeatureAddSub.h b/src/Mod/PartDesign/App/FeatureAddSub.h index fc5cc7e1cc..b912dd07c3 100644 --- a/src/Mod/PartDesign/App/FeatureAddSub.h +++ b/src/Mod/PartDesign/App/FeatureAddSub.h @@ -26,12 +26,15 @@ #include "FeatureRefine.h" +#include + /// Base class of all additive features in PartDesign namespace PartDesign { class PartDesignExport FeatureAddSub : public PartDesign::FeatureRefine { + Q_DECLARE_TR_FUNCTIONS(PartDesign::FeatureAddSub) PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::FeatureAddSub); public: @@ -42,12 +45,15 @@ public: FeatureAddSub(); + void onChanged(const App::Property *) override; Type getAddSubType(); short mustExecute() const override; virtual void getAddSubShape(Part::TopoShape &addShape, Part::TopoShape &subShape); + void updatePreviewShape() override; + Part::PropertyPartShape AddSubShape; diff --git a/src/Mod/PartDesign/App/FeatureDressUp.cpp b/src/Mod/PartDesign/App/FeatureDressUp.cpp index 4cf3ae246f..4eafd6a7d0 100644 --- a/src/Mod/PartDesign/App/FeatureDressUp.cpp +++ b/src/Mod/PartDesign/App/FeatureDressUp.cpp @@ -293,53 +293,62 @@ void DressUp::onChanged(const App::Property* prop) Feature::onChanged(prop); } -void DressUp::getAddSubShape(Part::TopoShape &addShape, Part::TopoShape &subShape) +void DressUp::getAddSubShape(Part::TopoShape& addShape, Part::TopoShape& subShape) { Part::TopoShape res = AddSubShape.getShape(); - if(res.isNull()) { + if (res.isNull()) { try { std::vector shapes; Part::TopoShape shape = Shape.getShape(); shape.setPlacement(Base::Placement()); - FeatureAddSub *base = nullptr; - if(SupportTransform.getValue()) { + FeatureAddSub* base = nullptr; + if (SupportTransform.getValue()) { // SupportTransform means transform the support together with // the dressing. So we need to find the previous support // feature (which must be of type FeatureAddSub), and skipping // any consecutive DressUp in-between. - for(Feature *current=this; ;current=static_cast(base)) { + for (Feature* current = this;; current = static_cast(base)) { base = freecad_cast(current->getBaseObject(true)); - if(!base) + if (!base) { FC_THROWM(Base::CADKernelError, - "Cannot find additive or subtractive support for " << getFullName()); - if(!base->isDerivedFrom()) + "Cannot find additive or subtractive support for " + << getFullName()); + } + if (!base->isDerivedFrom()) { break; + } } } Part::TopoShape baseShape; - if(base) { + if (base) { baseShape = base->getBaseTopoShape(true); baseShape.move(base->getLocation().Inverted()); if (base->getAddSubType() == Additive) { - if(!baseShape.isNull() && baseShape.hasSubShape(TopAbs_SOLID)) + if (!baseShape.isNull() && baseShape.hasSubShape(TopAbs_SOLID)) { shapes.emplace_back(shape.makeElementCut(baseShape.getShape())); - else + } + else { shapes.push_back(shape); - } else { + } + } + else { BRep_Builder builder; TopoDS_Compound comp; builder.MakeCompound(comp); // push an empty compound to indicate null additive shape shapes.emplace_back(comp); - if(!baseShape.isNull() && baseShape.hasSubShape(TopAbs_SOLID)) + if (!baseShape.isNull() && baseShape.hasSubShape(TopAbs_SOLID)) { shapes.emplace_back(baseShape.makeElementCut(shape.getShape())); - else + } + else { shapes.push_back(shape); + } } - } else { + } + else { baseShape = getBaseTopoShape(); baseShape.move(getLocation().Inverted()); shapes.emplace_back(shape.makeElementCut(baseShape.getShape())); @@ -350,34 +359,73 @@ void DressUp::getAddSubShape(Part::TopoShape &addShape, Part::TopoShape &subShap // bceause a dressing (e.g. a fillet) can either be additive or // subtractive. And the dressup feature can contain mixture of both. AddSubShape.setValue(Part::TopoShape().makeElementCompound(shapes)); - - } catch (Standard_Failure &e) { - FC_THROWM(Base::CADKernelError, "Failed to calculate AddSub shape: " - << e.GetMessageString()); + } + catch (Standard_Failure& e) { + FC_THROWM(Base::CADKernelError, + "Failed to calculate AddSub shape: " << e.GetMessageString()); } res = AddSubShape.getShape(); } - if(res.isNull()) + if (res.isNull()) { throw Part::NullShapeException("Null AddSub shape"); + } - if(res.getShape().ShapeType() != TopAbs_COMPOUND) { + if (res.getShape().ShapeType() != TopAbs_COMPOUND) { addShape = res; - } else { + } + else { int count = res.countSubShapes(TopAbs_SHAPE); - if(!count) + if (!count) { throw Part::NullShapeException("Null AddSub shape"); - if(count) { - Part::TopoShape s = res.getSubTopoShape(TopAbs_SHAPE, 1); - if(!s.isNull() && s.hasSubShape(TopAbs_SOLID)) - addShape = s; } - if(count > 1) { + if (count) { + Part::TopoShape s = res.getSubTopoShape(TopAbs_SHAPE, 1); + if (!s.isNull() && s.hasSubShape(TopAbs_SOLID)) { + addShape = s; + } + } + if (count > 1) { Part::TopoShape s = res.getSubTopoShape(TopAbs_SHAPE, 2); - if(!s.isNull() && s.hasSubShape(TopAbs_SOLID)) + if (!s.isNull() && s.hasSubShape(TopAbs_SOLID)) { subShape = s; + } } } } +void DressUp::updatePreviewShape() +{ + auto shape = Shape.getShape(); + auto baseFeature = freecad_cast(BaseFeature.getValue()); + + if (!baseFeature || baseFeature->Shape.getShape().isNull()) { + PreviewShape.setValue(Shape.getShape()); + return; + } + + std::vector faces, edges, vertices; + getGeneratedShapes(faces, edges, vertices); + + if (faces.empty()) { + PreviewShape.setValue(TopoDS_Shape()); + return; + } + + shape.setPlacement(Base::Placement()); + + BRep_Builder builder; + TopoDS_Compound comp; + builder.MakeCompound(comp); + + for (int faceId : faces) { + builder.Add(comp, shape.getSubShape(TopAbs_FACE, faceId)); + } + + Part::TopoShape preview(comp); + preview.mapSubElement(shape); + + PreviewShape.setValue(preview); } + +} // namespace PartDesign diff --git a/src/Mod/PartDesign/App/FeatureDressUp.h b/src/Mod/PartDesign/App/FeatureDressUp.h index a679b881ff..7be5e81641 100644 --- a/src/Mod/PartDesign/App/FeatureDressUp.h +++ b/src/Mod/PartDesign/App/FeatureDressUp.h @@ -65,6 +65,7 @@ public: std::vector getFaces(const TopoShape &shape); void getAddSubShape(Part::TopoShape &addShape, Part::TopoShape &subShape) override; + void updatePreviewShape() override; protected: void onChanged(const App::Property* prop) override; diff --git a/src/Mod/PartDesign/App/FeaturePrimitive.cpp b/src/Mod/PartDesign/App/FeaturePrimitive.cpp index bf8e664029..2ac3029dc0 100644 --- a/src/Mod/PartDesign/App/FeaturePrimitive.cpp +++ b/src/Mod/PartDesign/App/FeaturePrimitive.cpp @@ -150,10 +150,11 @@ App::DocumentObjectExecReturn* FeaturePrimitive::execute(const TopoDS_Shape& pri void FeaturePrimitive::onChanged(const App::Property* prop) { - if (prop == &AttachmentOffset){ - this->touch(); + if (prop == &AttachmentOffset) { + this->recompute(); return; } + FeatureAddSub::onChanged(prop); } diff --git a/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp b/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp index 994697683f..8c69705965 100644 --- a/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp +++ b/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp @@ -155,7 +155,6 @@ PyMOD_INIT_FUNC(PartDesignGui) PartDesignGui::ViewProviderSubShapeBinder::init(); PartDesignGui::ViewProviderSubShapeBinderPython::init(); PartDesignGui::ViewProviderBoolean ::init(); - PartDesignGui::ViewProviderAddSub ::init(); PartDesignGui::ViewProviderPrimitive ::init(); PartDesignGui::ViewProviderPipe ::init(); PartDesignGui::ViewProviderLoft ::init(); diff --git a/src/Mod/PartDesign/Gui/CMakeLists.txt b/src/Mod/PartDesign/Gui/CMakeLists.txt index 8a980b71e1..5b1d316c5e 100644 --- a/src/Mod/PartDesign/Gui/CMakeLists.txt +++ b/src/Mod/PartDesign/Gui/CMakeLists.txt @@ -109,8 +109,6 @@ SET(PartDesignGuiViewProvider_SRCS ViewProviderShapeBinder.cpp ViewProviderBoolean.cpp ViewProviderBoolean.h - ViewProviderAddSub.cpp - ViewProviderAddSub.h ViewProviderPrimitive.h ViewProviderPrimitive.cpp ViewProviderPipe.h diff --git a/src/Mod/PartDesign/Gui/TaskChamferParameters.cpp b/src/Mod/PartDesign/Gui/TaskChamferParameters.cpp index 41bd6b0393..5f2dc70ca4 100644 --- a/src/Mod/PartDesign/Gui/TaskChamferParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskChamferParameters.cpp @@ -182,7 +182,7 @@ void TaskChamferParameters::onCheckBoxUseAllEdgesToggled(bool checked) void TaskChamferParameters::setButtons(const selectionModes mode) { ui->buttonRefSel->setChecked(mode == refSel); - ui->buttonRefSel->setText(mode == refSel ? btnPreviewStr() : btnSelectStr()); + ui->buttonRefSel->setText(mode == refSel ? stopSelectionLabel() : startSelectionLabel()); } void TaskChamferParameters::onRefDeleted() @@ -349,7 +349,7 @@ bool TaskDlgChamferParameters::accept() { auto obj = getObject(); if (!obj->isError()) { - parameter->showObject(); + getViewObject()->showPreviousFeature(false); } parameter->apply(); diff --git a/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp b/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp index 0bd58df7c2..00116e55cf 100644 --- a/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskDraftParameters.cpp @@ -170,7 +170,7 @@ void TaskDraftParameters::onSelectionChanged(const Gui::SelectionChanges& msg) void TaskDraftParameters::setButtons(const selectionModes mode) { - ui->buttonRefSel->setText(mode == refSel ? btnPreviewStr() : btnSelectStr()); + ui->buttonRefSel->setText(mode == refSel ? stopSelectionLabel() : startSelectionLabel()); ui->buttonRefSel->setChecked(mode == refSel); ui->buttonLine->setChecked(mode == line); ui->buttonPlane->setChecked(mode == plane); @@ -180,7 +180,7 @@ void TaskDraftParameters::onButtonPlane(bool checked) { if (checked) { setButtons(plane); - hideObject(); + getViewObject()->showPreviousFeature(true); selectionMode = plane; Gui::Selection().clearSelection(); Gui::Selection().addSelectionGate(new ReferenceSelection( @@ -193,7 +193,7 @@ void TaskDraftParameters::onButtonLine(bool checked) { if (checked) { setButtons(line); - hideObject(); + getViewObject()->showPreviousFeature(true); selectionMode = line; Gui::Selection().clearSelection(); Gui::Selection().addSelectionGate( @@ -311,7 +311,7 @@ bool TaskDlgDraftParameters::accept() { auto tobj = getObject(); if (!tobj->isError()) { - parameter->showObject(); + getViewObject()->showPreviousFeature(false); } parameter->apply(); diff --git a/src/Mod/PartDesign/Gui/TaskDressUpParameters.cpp b/src/Mod/PartDesign/Gui/TaskDressUpParameters.cpp index 11247c81ef..32104377d7 100644 --- a/src/Mod/PartDesign/Gui/TaskDressUpParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskDressUpParameters.cpp @@ -55,10 +55,7 @@ using namespace Gui; /* TRANSLATOR PartDesignGui::TaskDressUpParameters */ TaskDressUpParameters::TaskDressUpParameters(ViewProviderDressUp *DressUpView, bool selectEdges, bool selectFaces, QWidget *parent) - : TaskBox(Gui::BitmapFactory().pixmap(DressUpView->featureIcon().c_str()), - DressUpView->menuName, - true, - parent) + : TaskFeatureParameters(DressUpView, parent, DressUpView->featureIcon(), DressUpView->menuName) , proxy(nullptr) , deleteAction(nullptr) , addAllEdgesAction(nullptr) @@ -70,7 +67,6 @@ TaskDressUpParameters::TaskDressUpParameters(ViewProviderDressUp *DressUpView, b App::GetApplication().getActiveTransaction(&transactionID); selectionMode = none; - showObject(); } TaskDressUpParameters::~TaskDressUpParameters() @@ -282,6 +278,16 @@ void TaskDressUpParameters::tryAddSelection(const std::string& doc, } } +QString TaskDressUpParameters::startSelectionLabel() +{ + return tr("Select"); +} + +QString TaskDressUpParameters::stopSelectionLabel() +{ + return tr("Confirm Selection"); +} + void TaskDressUpParameters::itemClickedTimeout() { // executed after double-click time passed wasDoubleClicked = false; @@ -368,34 +374,7 @@ void TaskDressUpParameters::removeItemFromListWidget(QListWidget* widget, const void TaskDressUpParameters::hideOnError() { App::DocumentObject* dressup = DressUpView->getObject(); - if (dressup->isError()) - hideObject(); - else - showObject(); -} - -void TaskDressUpParameters::setDressUpVisibility(bool visible) -{ - App::DocumentObject* base = getBase(); - if (base) { - App::DocumentObject* duv = DressUpView->getObject(); - if (duv->Visibility.getValue() != visible) { - duv->Visibility.setValue(visible); - } - if (base->Visibility.getValue() == visible) { - base->Visibility.setValue(!visible); - } - } -} - -void TaskDressUpParameters::hideObject() -{ - setDressUpVisibility(false); -} - -void TaskDressUpParameters::showObject() -{ - setDressUpVisibility(true); + DressUpView->setErrorState(dressup->isError()); } ViewProviderDressUp* TaskDressUpParameters::getDressUpView() const @@ -427,16 +406,12 @@ void TaskDressUpParameters::setSelectionMode(selectionModes mode) setButtons(mode); if (mode == none) { - showObject(); - Gui::Selection().rmvSelectionGate(); // remove any highlights and selections DressUpView->highlightReferences(false); } else { - hideObject(); - AllowSelectionFlags allow; allow.setFlag(AllowSelection::EDGE, allowEdges); allow.setFlag(AllowSelection::FACE, allowFaces); diff --git a/src/Mod/PartDesign/Gui/TaskDressUpParameters.h b/src/Mod/PartDesign/Gui/TaskDressUpParameters.h index 03ce9a1468..09c687287b 100644 --- a/src/Mod/PartDesign/Gui/TaskDressUpParameters.h +++ b/src/Mod/PartDesign/Gui/TaskDressUpParameters.h @@ -42,7 +42,7 @@ namespace Part { namespace PartDesignGui { -class TaskDressUpParameters : public Gui::TaskView::TaskBox, public Gui::SelectionObserver +class TaskDressUpParameters : public TaskFeatureParameters, public Gui::SelectionObserver { Q_OBJECT @@ -53,13 +53,8 @@ public: const std::vector getReferences() const; Part::Feature *getBase() const; - void hideObject(); - void showObject(); void setupTransaction(); - /// Apply the changes made to the object to it - virtual void apply() {} - int getTransactionID() const { return transactionID; } @@ -92,17 +87,6 @@ protected: ViewProviderDressUp* getDressUpView() const; - template T* getObject() const - { - static_assert(std::is_base_of::value, "Wrong template argument"); - - if (!DressUpView.expired()) { - return DressUpView->getObject(); - } - - return nullptr; - } - private: void tryAddSelection(const std::string& doc, const std::string& obj, const std::string& sub); void setDressUpVisibility(bool visible); @@ -116,8 +100,8 @@ protected: selectionModes selectionMode; int transactionID; - static const QString btnPreviewStr(); - static const QString btnSelectStr(); + static QString stopSelectionLabel(); + static QString startSelectionLabel(); private: Gui::WeakPtrT DressUpView; diff --git a/src/Mod/PartDesign/Gui/TaskExtrudeParameters.cpp b/src/Mod/PartDesign/Gui/TaskExtrudeParameters.cpp index cb9b734649..320e68d05f 100644 --- a/src/Mod/PartDesign/Gui/TaskExtrudeParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskExtrudeParameters.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include "ui_TaskPadPocketParameters.h" #include "TaskExtrudeParameters.h" diff --git a/src/Mod/PartDesign/Gui/TaskFeatureParameters.cpp b/src/Mod/PartDesign/Gui/TaskFeatureParameters.cpp index 4585ef4924..8e71656e8d 100644 --- a/src/Mod/PartDesign/Gui/TaskFeatureParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskFeatureParameters.cpp @@ -52,6 +52,9 @@ TaskFeatureParameters::TaskFeatureParameters(PartDesignGui::ViewProvider *vp, QW { Gui::Document* doc = vp->getDocument(); this->attachDocument(doc); + + vp->showPreviousFeature(true); + vp->showPreview(true); } void TaskFeatureParameters::slotDeletedObject(const Gui::ViewProviderDocumentObject& Obj) diff --git a/src/Mod/PartDesign/Gui/TaskFilletParameters.cpp b/src/Mod/PartDesign/Gui/TaskFilletParameters.cpp index d1f9cd5cc4..23d4f7ca7c 100644 --- a/src/Mod/PartDesign/Gui/TaskFilletParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskFilletParameters.cpp @@ -134,7 +134,7 @@ void TaskFilletParameters::onCheckBoxUseAllEdgesToggled(bool checked) void TaskFilletParameters::setButtons(const selectionModes mode) { ui->buttonRefSel->setChecked(mode == refSel); - ui->buttonRefSel->setText(mode == refSel ? btnPreviewStr() : btnSelectStr()); + ui->buttonRefSel->setText(mode == refSel ? stopSelectionLabel() : startSelectionLabel()); } void TaskFilletParameters::onRefDeleted() @@ -216,7 +216,7 @@ bool TaskDlgFilletParameters::accept() { auto obj = getObject(); if (!obj->isError()) { - parameter->showObject(); + getViewObject()->showPreviousFeature(false); } parameter->apply(); diff --git a/src/Mod/PartDesign/Gui/TaskHelixParameters.ui b/src/Mod/PartDesign/Gui/TaskHelixParameters.ui index e9a7c6c267..69bc5fbfbd 100644 --- a/src/Mod/PartDesign/Gui/TaskHelixParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskHelixParameters.ui @@ -286,7 +286,7 @@ - Update view + Recompute on change true diff --git a/src/Mod/PartDesign/Gui/TaskHoleParameters.cpp b/src/Mod/PartDesign/Gui/TaskHoleParameters.cpp index f17ae611f1..defbe6b3a6 100644 --- a/src/Mod/PartDesign/Gui/TaskHoleParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskHoleParameters.cpp @@ -241,8 +241,6 @@ TaskHoleParameters::TaskHoleParameters(ViewProviderHole* HoleView, QWidget* pare this, &TaskHoleParameters::baseProfileTypeChanged); // clang-format on - getViewObject()->show(); - ui->Diameter->bind(pcHole->Diameter); ui->HoleCutDiameter->bind(pcHole->HoleCutDiameter); ui->HoleCutDepth->bind(pcHole->HoleCutDepth); diff --git a/src/Mod/PartDesign/Gui/TaskLoftParameters.ui b/src/Mod/PartDesign/Gui/TaskLoftParameters.ui index 6a0588a349..3a44b1105a 100644 --- a/src/Mod/PartDesign/Gui/TaskLoftParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskLoftParameters.ui @@ -117,7 +117,7 @@ - Update view + Recompute on change true diff --git a/src/Mod/PartDesign/Gui/TaskPadPocketParameters.ui b/src/Mod/PartDesign/Gui/TaskPadPocketParameters.ui index 22d3dd492a..a8e09710c3 100644 --- a/src/Mod/PartDesign/Gui/TaskPadPocketParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskPadPocketParameters.ui @@ -468,7 +468,7 @@ measured along the specified direction - Update view + Recompute on change true diff --git a/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp b/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp index 73ffdfc834..c30902fd2e 100644 --- a/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp @@ -52,6 +52,9 @@ TaskBoxPrimitives::TaskBoxPrimitives(ViewProviderPrimitive* vp, QWidget* parent) , ui(new Ui_DlgPrimitives) , vp(vp) { + vp->showPreview(true); + vp->showPreviousFeature(true); + proxy = new QWidget(this); ui->setupUi(proxy); @@ -972,8 +975,9 @@ bool TaskBoxPrimitives::setPrimitive(App::DocumentObject* obj) return true; } -TaskPrimitiveParameters::TaskPrimitiveParameters(ViewProviderPrimitive* PrimitiveView) - : vp_prm(PrimitiveView) +TaskDlgPrimitiveParameters::TaskDlgPrimitiveParameters(ViewProviderPrimitive* PrimitiveView) + : TaskDlgFeatureParameters(PrimitiveView) + , vp_prm(PrimitiveView) { assert(PrimitiveView); @@ -983,9 +987,9 @@ TaskPrimitiveParameters::TaskPrimitiveParameters(ViewProviderPrimitive* Primitiv Content.push_back(parameter); } -TaskPrimitiveParameters::~TaskPrimitiveParameters() = default; +TaskDlgPrimitiveParameters::~TaskDlgPrimitiveParameters() = default; -bool TaskPrimitiveParameters::accept() +bool TaskDlgPrimitiveParameters::accept() { bool primitiveOK = primitive->setPrimitive(vp_prm->getObject()); if (!primitiveOK) { @@ -997,7 +1001,7 @@ bool TaskPrimitiveParameters::accept() return true; } -bool TaskPrimitiveParameters::reject() +bool TaskDlgPrimitiveParameters::reject() { // roll back the done things Gui::Command::abortCommand(); @@ -1006,7 +1010,7 @@ bool TaskPrimitiveParameters::reject() return true; } -QDialogButtonBox::StandardButtons TaskPrimitiveParameters::getStandardButtons() const +QDialogButtonBox::StandardButtons TaskDlgPrimitiveParameters::getStandardButtons() const { return Gui::TaskView::TaskDialog::getStandardButtons(); } diff --git a/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.h b/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.h index 06fc7bb9a8..a0da4a3f1f 100644 --- a/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.h +++ b/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.h @@ -31,6 +31,7 @@ #include "ViewProviderPrimitive.h" #include "TaskDatumParameters.h" +#include "TaskFeatureParameters.h" namespace App { class Property; @@ -117,13 +118,13 @@ private: ViewProviderPrimitive* vp; }; -class TaskPrimitiveParameters : public Gui::TaskView::TaskDialog +class TaskDlgPrimitiveParameters : public TaskDlgFeatureParameters { Q_OBJECT public: - explicit TaskPrimitiveParameters(ViewProviderPrimitive *PrimitiveView); - ~TaskPrimitiveParameters() override; + explicit TaskDlgPrimitiveParameters(ViewProviderPrimitive *PrimitiveView); + ~TaskDlgPrimitiveParameters() override; protected: QDialogButtonBox::StandardButtons getStandardButtons() const override; diff --git a/src/Mod/PartDesign/Gui/TaskRevolutionParameters.ui b/src/Mod/PartDesign/Gui/TaskRevolutionParameters.ui index 82cbbb0c57..dcd94bc59b 100644 --- a/src/Mod/PartDesign/Gui/TaskRevolutionParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskRevolutionParameters.ui @@ -189,7 +189,7 @@ - Update view + Recompute on change true diff --git a/src/Mod/PartDesign/Gui/TaskSketchBasedParameters.cpp b/src/Mod/PartDesign/Gui/TaskSketchBasedParameters.cpp index e9c8d55dfa..1de7a06db9 100644 --- a/src/Mod/PartDesign/Gui/TaskSketchBasedParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskSketchBasedParameters.cpp @@ -90,22 +90,37 @@ const QString TaskSketchBasedParameters::onAddSelection(const Gui::SelectionChan void TaskSketchBasedParameters::startReferenceSelection(App::DocumentObject* profile, App::DocumentObject* base) { - if (Gui::Document* doc = getGuiDocument()) { - doc->setHide(profile->getNameInDocument()); - if (base) { - doc->setShow(base->getNameInDocument()); + const auto* bodyViewProvider = getViewObject()->getBodyViewProvider(); + + previouslyVisibleViewProvider = bodyViewProvider->getShownViewProvider(); + + if (!base) { + return; + } + + if (Document* doc = getGuiDocument()) { + if (previouslyVisibleViewProvider) { + previouslyVisibleViewProvider->hide(); } + + doc->setShow(base->getNameInDocument()); } } void TaskSketchBasedParameters::finishReferenceSelection(App::DocumentObject* profile, App::DocumentObject* base) { - if (Gui::Document* doc = getGuiDocument()) { - doc->setShow(profile->getNameInDocument()); + if (!previouslyVisibleViewProvider) { + return; + } + + if (Document* doc = getGuiDocument()) { if (base) { doc->setHide(base->getNameInDocument()); } + + previouslyVisibleViewProvider->show(); + previouslyVisibleViewProvider = nullptr; } } diff --git a/src/Mod/PartDesign/Gui/TaskSketchBasedParameters.h b/src/Mod/PartDesign/Gui/TaskSketchBasedParameters.h index 6a2c2067bd..7d8acf9516 100644 --- a/src/Mod/PartDesign/Gui/TaskSketchBasedParameters.h +++ b/src/Mod/PartDesign/Gui/TaskSketchBasedParameters.h @@ -72,6 +72,9 @@ protected: /// or the subelement's name if the object is a solid. QString make2DLabel(const App::DocumentObject* section, const std::vector& subValues); + +private: + Gui::ViewProvider* previouslyVisibleViewProvider {nullptr}; }; class TaskDlgSketchBasedParameters : public PartDesignGui::TaskDlgFeatureParameters diff --git a/src/Mod/PartDesign/Gui/TaskThicknessParameters.cpp b/src/Mod/PartDesign/Gui/TaskThicknessParameters.cpp index b44add0142..24a5c8aa1a 100644 --- a/src/Mod/PartDesign/Gui/TaskThicknessParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskThicknessParameters.cpp @@ -144,7 +144,7 @@ void TaskThicknessParameters::onSelectionChanged(const Gui::SelectionChanges& ms void TaskThicknessParameters::setButtons(const selectionModes mode) { ui->buttonRefSel->setChecked(mode == refSel); - ui->buttonRefSel->setText(mode == refSel ? btnPreviewStr() : btnSelectStr()); + ui->buttonRefSel->setText(mode == refSel ? stopSelectionLabel() : startSelectionLabel()); } void TaskThicknessParameters::onRefDeleted() @@ -280,7 +280,7 @@ bool TaskDlgThicknessParameters::accept() { auto obj = getObject(); if (!obj->isError()) { - parameter->showObject(); + getViewObject()->showPreviousFeature(false); } parameter->apply(); diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.ui b/src/Mod/PartDesign/Gui/TaskTransformedParameters.ui index a449d6cdc1..469505adb5 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.ui @@ -119,7 +119,7 @@ - Update view + Recompute on change true diff --git a/src/Mod/PartDesign/Gui/ViewProvider.cpp b/src/Mod/PartDesign/Gui/ViewProvider.cpp index a5ad685d3f..c97c167193 100644 --- a/src/Mod/PartDesign/Gui/ViewProvider.cpp +++ b/src/Mod/PartDesign/Gui/ViewProvider.cpp @@ -26,8 +26,10 @@ #ifndef _PreComp_ # include # include -# include # include +# include +# include +# include #endif #include @@ -37,15 +39,23 @@ #include #include #include +#include +#include #include +#include #include -#include +#include +#include +#include +#include +#include #include "TaskFeatureParameters.h" #include "ViewProvider.h" #include "ViewProviderPy.h" + using namespace PartDesignGui; PROPERTY_SOURCE_WITH_EXTENSIONS(PartDesignGui::ViewProvider, PartGui::ViewProviderPart) @@ -53,11 +63,31 @@ PROPERTY_SOURCE_WITH_EXTENSIONS(PartDesignGui::ViewProvider, PartGui::ViewProvid ViewProvider::ViewProvider() { ViewProviderSuppressibleExtension::initExtension(this); - PartGui::ViewProviderAttachExtension::initExtension(this); + ViewProviderAttachExtension::initExtension(this); + ViewProviderPreviewExtension::initExtension(this); } ViewProvider::~ViewProvider() = default; +void ViewProvider::beforeDelete() +{ + ViewProviderPart::beforeDelete(); +} + +void ViewProvider::attach(App::DocumentObject* pcObject) +{ + ViewProviderPart::attach(pcObject); + + if (auto addSubFeature = getObject()) { + const Base::Color green(0.0F, 1.0F, 0.6F); + const Base::Color red(1.0F, 0.0F, 0.0F); + + bool isAdditive = addSubFeature->getAddSubType() == PartDesign::FeatureAddSub::Additive; + + PreviewColor.setValue(isAdditive ? green : red); + } +} + bool ViewProvider::doubleClicked() { try { @@ -149,6 +179,8 @@ TaskDlgFeatureParameters *ViewProvider::getEditDialog() { void ViewProvider::unsetEdit(int ModNum) { + showPreview(false); + // return to the WB we were in before editing the PartDesign feature if (!oldWb.empty()) { Gui::Command::assureWorkbench(oldWb.c_str()); @@ -170,15 +202,42 @@ void ViewProvider::unsetEdit(int ModNum) void ViewProvider::updateData(const App::Property* prop) { - // TODO What's that? (2015-07-24, Fat-Zer) - if (prop->is() && - strcmp(prop->getName(),"AddSubShape") == 0) { - return; + if (strcmp(prop->getName(), "PreviewShape") == 0) { + updatePreviewShape(); } inherited::updateData(prop); } +void ViewProvider::attachPreview() +{ + ViewProviderPreviewExtension::attachPreview(); + + pcToolPreview = new PartGui::SoPreviewShape; + pcToolPreview->transparency = 0.95F; + pcToolPreview->color.connectFrom(&pcPreviewShape->color); + + pcPreviewRoot->addChild(pcToolPreview); +} + +void ViewProvider::updatePreview() +{ + ViewProviderPreviewExtension::updatePreview(); + + if (auto* addSubFeature = getObject()) { + // we only want to show the additional tool preview for subtractive features + if (addSubFeature->getAddSubType() != PartDesign::FeatureAddSub::Subtractive) { + return; + } + + Part::TopoShape toolShape = addSubFeature->AddSubShape.getShape(); + + updatePreviewShape(toolShape, pcToolPreview); + } else { + updatePreviewShape({}, pcToolPreview); + } +} + void ViewProvider::onChanged(const App::Property* prop) { //if the object is inside of a body we make sure it is the only visible one on activation @@ -267,12 +326,52 @@ bool ViewProvider::onDelete(const std::vector&) // // fixes (#3084) - FCMD_OBJ_CMD(body,"removeObject(" << Gui::Command::getObjectCmd(feature) << ')'); + FCMD_OBJ_CMD(body, "removeObject(" << Gui::Command::getObjectCmd(feature) << ')'); } return true; } +Part::TopoShape ViewProvider::getPreviewShape() const +{ + if (auto feature = getObject()->getExtensionByType(true)) { + // Feature is responsible for generating proper shape and this ViewProvider + // is using it instead of more normal `Shape` property. + return feature->PreviewShape.getShape(); + } + + return {}; +} + +void ViewProvider::showPreviousFeature(bool enable) +{ + PartDesign::Feature* feature {getObject()}; + PartDesign::Feature* baseFeature { nullptr }; + + ViewProvider* baseFeatureViewProvider { nullptr }; + + if (!feature) { + return; + } + + baseFeature = dynamic_cast(feature->BaseFeature.getValue()); + if (baseFeature) { + baseFeatureViewProvider = freecad_cast(Gui::Application::Instance->getViewProvider(baseFeature)); + } + + if (!baseFeatureViewProvider) { + baseFeatureViewProvider = this; + } + + if (enable) { + baseFeatureViewProvider->show(); + hide(); + } else { + baseFeatureViewProvider->hide(); + show(); + } +} + void ViewProvider::setBodyMode(bool bodymode) { std::vector props; @@ -331,8 +430,6 @@ ViewProviderBody* ViewProvider::getBodyViewProvider() { return nullptr; } - - namespace Gui { /// @cond DOXERR PROPERTY_SOURCE_TEMPLATE(PartDesignGui::ViewProviderPython, PartDesignGui::ViewProvider) diff --git a/src/Mod/PartDesign/Gui/ViewProvider.h b/src/Mod/PartDesign/Gui/ViewProvider.h index 773d4aef78..c09c30978b 100644 --- a/src/Mod/PartDesign/Gui/ViewProvider.h +++ b/src/Mod/PartDesign/Gui/ViewProvider.h @@ -24,12 +24,16 @@ #ifndef PARTGUI_ViewProvider_H #define PARTGUI_ViewProvider_H -#include -#include "ViewProviderBody.h" +#include #include -#include "Gui/ViewProviderSuppressibleExtension.h" - +#include +#include #include +#include +#include + +#include "ViewProviderBody.h" + namespace PartDesignGui { @@ -40,7 +44,8 @@ class TaskDlgFeatureParameters; */ class PartDesignGuiExport ViewProvider : public PartGui::ViewProviderPart, Gui::ViewProviderSuppressibleExtension, - PartGui::ViewProviderAttachExtension + PartGui::ViewProviderAttachExtension, + public PartGui::ViewProviderPreviewExtension { using inherited = PartGui::ViewProviderPart; PROPERTY_HEADER_WITH_OVERRIDE(PartDesignGui::ViewProvider); @@ -51,8 +56,10 @@ public: /// destructor ~ViewProvider() override; + void beforeDelete() override; + void attach(App::DocumentObject* pcObject) override; + bool doubleClicked() override; - void updateData(const App::Property*) override; void onChanged(const App::Property* prop) override; Gui::ViewProvider* startEditing(int ModNum) override; @@ -73,6 +80,11 @@ public: //Returns the ViewProvider of the body the feature belongs to, or NULL, if not in a body ViewProviderBody* getBodyViewProvider(); + /// Provides preview shape + Part::TopoShape getPreviewShape() const override; + /// Toggles visibility of the preview + void showPreviousFeature(bool); + PyObject* getPyObject() override; QIcon mergeColorfulOverlayIcons (const QIcon & orig) const override; @@ -81,6 +93,10 @@ protected: void setupContextMenu(QMenu* menu, QObject* receiver, const char* member) override; bool setEdit(int ModNum) override; void unsetEdit(int ModNum) override; + void updateData(const App::Property* prop) override; + + void attachPreview() override; + void updatePreview() override; bool onDelete(const std::vector &) override; @@ -92,7 +108,11 @@ protected: std::string oldWb; ViewProvider* previouslyShownViewProvider { nullptr }; + bool isSetTipIcon { false }; + +private: + Gui::CoinPtr pcToolPreview; }; using ViewProviderPython = Gui::ViewProviderFeaturePythonT; diff --git a/src/Mod/PartDesign/Gui/ViewProviderAddSub.cpp b/src/Mod/PartDesign/Gui/ViewProviderAddSub.cpp deleted file mode 100644 index 250771aa5a..0000000000 --- a/src/Mod/PartDesign/Gui/ViewProviderAddSub.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2015 Stefan Tröger * - * * - * 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 -#endif - -#include -#include -#include -#include -#include -#include - -#include "ViewProviderAddSub.h" - - -using namespace PartDesignGui; - -PROPERTY_SOURCE(PartDesignGui::ViewProviderAddSub,PartDesignGui::ViewProvider) - -ViewProviderAddSub::ViewProviderAddSub() -{ - previewShape = new SoSeparator(); - previewShape->ref(); - previewFaceSet = new PartGui::SoBrepFaceSet(); - previewFaceSet->ref(); - previewCoords = new SoCoordinate3(); - previewCoords->ref(); - previewNorm = new SoNormal(); - previewNorm->ref(); - whichChild = -1; -} - -ViewProviderAddSub::~ViewProviderAddSub() -{ - previewFaceSet->unref(); - previewCoords->unref(); - previewNorm->unref(); - previewShape->unref(); -} - -void ViewProviderAddSub::attach(App::DocumentObject* obj) { - - ViewProvider::attach(obj); - - auto* bind = new SoMaterialBinding(); - bind->value = SoMaterialBinding::OVERALL; - auto* material = new SoMaterial(); - if (static_cast(obj)->getAddSubType() == PartDesign::FeatureAddSub::Additive) - material->diffuseColor = SbColor(1,1,0); - else - material->diffuseColor = SbColor(1,0,0); - - material->transparency = 0.7f; - auto* pick = new SoPickStyle(); - pick->style = SoPickStyle::UNPICKABLE; - - previewShape->addChild(pick); - previewShape->addChild(bind); - previewShape->addChild(material); - previewShape->addChild(previewCoords); - previewShape->addChild(previewNorm); - previewShape->addChild(previewFaceSet); - - addDisplayMaskMode(previewShape, "Shape preview"); - updateAddSubShapeIndicator(); -} - -void ViewProviderAddSub::updateAddSubShapeIndicator() { - - TopoDS_Shape cShape(getObject()->AddSubShape.getValue()); - if (cShape.IsNull()) { - previewCoords ->point .setNum(0); - previewNorm ->vector .setNum(0); - previewFaceSet ->coordIndex .setNum(0); - previewFaceSet ->partIndex .setNum(0); - return; - } - - try { - // calculating the deflection value - Bnd_Box bounds; - BRepBndLib::Add(cShape, bounds); - bounds.SetGap(0.0); - Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; - bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); - Standard_Real deflection = ((xMax-xMin)+(yMax-yMin)+(zMax-zMin))/300.0 * Deviation.getValue(); - - // create or use the mesh on the data structure - Standard_Real AngDeflectionRads = Base::toRadians(AngularDeflection.getValue()); - BRepMesh_IncrementalMesh(cShape, deflection, Standard_False, AngDeflectionRads, Standard_True); - - // We must reset the location here because the transformation data - // are set in the placement property - TopLoc_Location aLoc; - cShape.Location(aLoc); - - // count triangles and nodes in the mesh - int numTriangles=0,numNodes=0,numNorms=0,numFaces=0; - TopExp_Explorer Ex; - for (Ex.Init(cShape,TopAbs_FACE);Ex.More();Ex.Next()) { - Handle (Poly_Triangulation) mesh = BRep_Tool::Triangulation(TopoDS::Face(Ex.Current()), aLoc); - // Note: we must also count empty faces - if (!mesh.IsNull()) { - numTriangles += mesh->NbTriangles(); - numNodes += mesh->NbNodes(); - numNorms += mesh->NbNodes(); - } - numFaces++; - } - - // create memory for the nodes and indexes - previewCoords ->point .setNum(numNodes); - previewNorm ->vector .setNum(numNorms); - previewFaceSet ->coordIndex .setNum(numTriangles*4); - previewFaceSet ->partIndex .setNum(numFaces); - - // get the raw memory for fast fill up - SbVec3f* verts = previewCoords ->point .startEditing(); - SbVec3f* previewNorms = previewNorm ->vector .startEditing(); - int32_t* index = previewFaceSet ->coordIndex .startEditing(); - int32_t* parts = previewFaceSet ->partIndex .startEditing(); - - // preset the previewNormal vector with null vector - for (int i=0;i < numNorms;i++) - previewNorms[i]= SbVec3f(0.0,0.0,0.0); - - int ii = 0,faceNodeOffset=0,faceTriaOffset=0; - for (Ex.Init(cShape, TopAbs_FACE); Ex.More(); Ex.Next(),ii++) { - const TopoDS_Face &actFace = TopoDS::Face(Ex.Current()); - - TopLoc_Location loc; - Handle(Poly_Triangulation) mesh = BRep_Tool::Triangulation(actFace, loc); - if (mesh.IsNull()) - continue; - - // get triangulation - std::vector points; - std::vector facets; - Part::Tools::getTriangulation(actFace, points, facets); - - // get normal per vertex - std::vector vertexnormals; - Part::Tools::getPointNormals(actFace, mesh, vertexnormals); - Part::Tools::applyTransformationOnNormals(loc, vertexnormals); - - // getting size of node and triangle array of this face - std::size_t nbNodesInFace = points.size(); - std::size_t nbTriInFace = facets.size(); - - for (std::size_t i = 0; i < points.size(); i++) { - verts[faceNodeOffset+i] = SbVec3f(points[i].X(), points[i].Y(), points[i].Z()); - } - - for (std::size_t i = 0; i < vertexnormals.size(); i++) { - previewNorms[faceNodeOffset+i] = SbVec3f(vertexnormals[i].X(), vertexnormals[i].Y(), vertexnormals[i].Z()); - } - - // cycling through the poly mesh - for (std::size_t g=0; g < nbTriInFace; g++) { - // Get the triangle - Standard_Integer N1,N2,N3; - facets[g].Get(N1,N2,N3); - - // set the index vector with the 3 point indexes and the end delimiter - index[faceTriaOffset*4+4*g] = faceNodeOffset+N1; - index[faceTriaOffset*4+4*g+1] = faceNodeOffset+N2; - index[faceTriaOffset*4+4*g+2] = faceNodeOffset+N3; - index[faceTriaOffset*4+4*g+3] = SO_END_FACE_INDEX; - } - - parts[ii] = nbTriInFace; // new part - - // counting up the per Face offsets - faceNodeOffset += nbNodesInFace; - faceTriaOffset += nbTriInFace; - } - - // previewNormalize all previewNormals - for (int i = 0; i< numNorms ;i++) - previewNorms[i].normalize(); - - // end the editing of the nodes - previewCoords ->point .finishEditing(); - previewNorm ->vector .finishEditing(); - previewFaceSet ->coordIndex .finishEditing(); - previewFaceSet ->partIndex .finishEditing(); - } - catch (...) { - Base::Console().error("Cannot compute Inventor representation for the shape of %s.\n",pcObject->getNameInDocument()); - } -} - -void ViewProviderAddSub::updateData(const App::Property* p) { - - if(p->getName() && strcmp(p->getName(), "AddSubShape")==0) - updateAddSubShapeIndicator(); - - PartDesignGui::ViewProvider::updateData(p); -} - -void ViewProviderAddSub::setPreviewDisplayMode(bool onoff) { - // A mask mode is always set, also for hidden objects. - // Now when changing to another mask mode this automatically - // displays an object and when restoring the previous state it's - // not sufficient to only revert the mask mode. Also the child - // number of the switch node must be reverted. - if (onoff) { - if(pcModeSwitch->getChild(getDefaultMode()) == previewShape) - return; - displayMode = getActiveDisplayMode(); - whichChild = pcModeSwitch->whichChild.getValue(); - setDisplayMaskMode("Shape preview"); - } - - if (!onoff) { - if(pcModeSwitch->getChild(getDefaultMode()) != previewShape) - return; - setDisplayMaskMode(displayMode.c_str()); - pcModeSwitch->whichChild.setValue(whichChild); - } - - App::DocumentObject* obj = getObject()->BaseFeature.getValue(); - if (obj) - static_cast(Gui::Application::Instance->getViewProvider(obj))->makeTemporaryVisible(onoff); -} diff --git a/src/Mod/PartDesign/Gui/ViewProviderAddSub.h b/src/Mod/PartDesign/Gui/ViewProviderAddSub.h deleted file mode 100644 index d5d6504cdf..0000000000 --- a/src/Mod/PartDesign/Gui/ViewProviderAddSub.h +++ /dev/null @@ -1,62 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2015 Stefan Tröger * - * * - * 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 * - * * - ***************************************************************************/ - - -#ifndef PARTGUI_ViewProviderAddSub_H -#define PARTGUI_ViewProviderAddSub_H - -#include "ViewProvider.h" -#include - -namespace PartDesignGui { - -class PartDesignGuiExport ViewProviderAddSub : public ViewProvider -{ - PROPERTY_HEADER_WITH_OVERRIDE(PartDesignGui::ViewProviderAddSub); - -public: - /// constructor - ViewProviderAddSub(); - /// destructor - ~ViewProviderAddSub() override; - - void attach(App::DocumentObject*) override; - void updateData(const App::Property*) override; - -protected: - void updateAddSubShapeIndicator(); - void setPreviewDisplayMode(bool); - - SoSeparator* previewShape; - PartGui::SoBrepFaceSet* previewFaceSet; - SoCoordinate3* previewCoords; - SoNormal* previewNorm; - -private: - int whichChild; - std::string displayMode; -}; - -} // namespace PartDesignGui - - -#endif // PARTGUI_ViewProviderBoolean_H diff --git a/src/Mod/PartDesign/Gui/ViewProviderDressUp.cpp b/src/Mod/PartDesign/Gui/ViewProviderDressUp.cpp index 0e9f5c53b2..199226c984 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderDressUp.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderDressUp.cpp @@ -39,11 +39,23 @@ #include "ViewProviderDressUp.h" #include "TaskDressUpParameters.h" +#include + using namespace PartDesignGui; -PROPERTY_SOURCE(PartDesignGui::ViewProviderDressUp,PartDesignGui::ViewProvider) +PROPERTY_SOURCE(PartDesignGui::ViewProviderDressUp, PartDesignGui::ViewProvider) +void ViewProviderDressUp::attach(App::DocumentObject* pcObject) +{ + ViewProvider::attach(pcObject); + + const Base::Color magenta(1.0F, 0.0F, 1.0F); + PreviewColor.setValue(magenta); + + setErrorState(false); +} + void ViewProviderDressUp::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { QString text = QString::fromStdString(getObject()->Label.getStrValue()); @@ -121,3 +133,13 @@ void ViewProviderDressUp::highlightReferences(const bool on) } } +void ViewProviderDressUp::setErrorState(bool error) +{ + const Base::Color red(1.0, 0.0, 0.0); + + constexpr float errorTransparency = 0.95F; + + pcPreviewShape->transparency = error ? errorTransparency : PartGui::SoPreviewShape::defaultTransparency; + pcPreviewShape->color = Base::convertTo(error ? red : PreviewColor.getValue()); +} + diff --git a/src/Mod/PartDesign/Gui/ViewProviderDressUp.h b/src/Mod/PartDesign/Gui/ViewProviderDressUp.h index 0096d2cc0a..c38ec1cc9a 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderDressUp.h +++ b/src/Mod/PartDesign/Gui/ViewProviderDressUp.h @@ -40,7 +40,9 @@ public: /// constructor ViewProviderDressUp() = default; /// destructor - ~ViewProviderDressUp() override = default; + ~ViewProviderDressUp() override = default; + + void attach(App::DocumentObject* pcObject) override; /// grouping handling void setupContextMenu(QMenu*, QObject*, const char*) override; @@ -48,6 +50,9 @@ public: /// Highlight the references that have been selected void highlightReferences(const bool on); + /// Set preview parameters to indicate error state + void setErrorState(bool error); + /** * Returns the feature Name associated with the view provider. * Should be reimplemented in the successor. diff --git a/src/Mod/PartDesign/Gui/ViewProviderHelix.cpp b/src/Mod/PartDesign/Gui/ViewProviderHelix.cpp index 9d62d3b51c..53e6da3382 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderHelix.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderHelix.cpp @@ -49,12 +49,12 @@ ViewProviderHelix::~ViewProviderHelix() = default; void ViewProviderHelix::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { addDefaultAction(menu, QObject::tr("Edit Helix")); - PartDesignGui::ViewProviderAddSub::setupContextMenu(menu, receiver, member); + ViewProvider::setupContextMenu(menu, receiver, member); } TaskDlgFeatureParameters *ViewProviderHelix::getEditDialog() { - return new TaskDlgHelixParameters( this ); + return new TaskDlgHelixParameters(this); } QIcon ViewProviderHelix::getIcon() const { @@ -69,23 +69,6 @@ QIcon ViewProviderHelix::getIcon() const { return PartDesignGui::ViewProvider::mergeGreyableOverlayIcons(Gui::BitmapFactory().pixmap(str.toStdString().c_str())); } -bool ViewProviderHelix::setEdit(int ModNum) -{ - if (ModNum == ViewProvider::Default ) { - auto* prim = getObject(); - setPreviewDisplayMode(TaskHelixParameters::showPreview(prim)); - } - return ViewProviderAddSub::setEdit(ModNum); -} - -void ViewProviderHelix::unsetEdit(int ModNum) -{ - setPreviewDisplayMode(false); - // Rely on parent class to: - // restitute old workbench (set setEdit above) and close the dialog if exiting editing - PartDesignGui::ViewProvider::unsetEdit(ModNum); -} - std::vector ViewProviderHelix::claimChildren() const { std::vector temp; App::DocumentObject* sketch = getObject()->Profile.getValue(); diff --git a/src/Mod/PartDesign/Gui/ViewProviderHelix.h b/src/Mod/PartDesign/Gui/ViewProviderHelix.h index 675e49f603..979ebaaf6c 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderHelix.h +++ b/src/Mod/PartDesign/Gui/ViewProviderHelix.h @@ -24,12 +24,12 @@ #ifndef PARTGUI_ViewProviderHelix_H #define PARTGUI_ViewProviderHelix_H -#include "ViewProviderAddSub.h" +#include "ViewProvider.h" namespace PartDesignGui { -class PartDesignGuiExport ViewProviderHelix : public ViewProviderAddSub +class PartDesignGuiExport ViewProviderHelix : public ViewProvider { PROPERTY_HEADER_WITH_OVERRIDE(PartDesignGui::ViewProviderHelix); @@ -51,8 +51,6 @@ protected: /// Returns a newly created TaskDlgHelixParameters TaskDlgFeatureParameters *getEditDialog() override; - bool setEdit(int ModNum) override; - void unsetEdit(int ModNum) override; }; diff --git a/src/Mod/PartDesign/Gui/ViewProviderHole.cpp b/src/Mod/PartDesign/Gui/ViewProviderHole.cpp index 2e5a011481..736abd014a 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderHole.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderHole.cpp @@ -63,59 +63,24 @@ void ViewProviderHole::setupContextMenu(QMenu* menu, QObject* receiver, const ch PartGui::ViewProviderPart::setupContextMenu(menu, receiver, member); // clazy:exclude=skipped-base-method } -bool ViewProviderHole::setEdit(int ModNum) -{ - if (ModNum == ViewProvider::Default ) { - // When double-clicking on the item for this hole the - // object unsets and sets its edit mode without closing - // the task panel - Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); - TaskDlgHoleParameters *holeDlg = qobject_cast(dlg); - if (holeDlg && holeDlg->getViewObject() != this) - holeDlg = nullptr; // another hole left open its task panel - if (dlg && !holeDlg) { - QMessageBox msgBox(Gui::getMainWindow()); - msgBox.setText(QObject::tr("A dialog is already open in the task panel")); - msgBox.setInformativeText(QObject::tr("Close this dialog?")); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::Yes); - int ret = msgBox.exec(); - if (ret == QMessageBox::Yes) - Gui::Control().closeDialog(); - else - return false; - } - - // clear the selection (convenience) - Gui::Selection().clearSelection(); - - // always change to PartDesign WB, remember where we come from - oldWb = Gui::Command::assureWorkbench("PartDesignWorkbench"); - - // start the edit dialog - if (holeDlg) - Gui::Control().showDialog(holeDlg); - else - Gui::Control().showDialog(new TaskDlgHoleParameters(this)); - - return true; - } - else { - return PartGui::ViewProviderPart::setEdit(ModNum); // clazy:exclude=skipped-base-method - } -} - -bool ViewProviderHole::onDelete(const std::vector &s) +bool ViewProviderHole::onDelete(const std::vector& s) { // get the Sketch PartDesign::Hole* pcHole = getObject(); - Sketcher::SketchObject *pcSketch = nullptr; - if (pcHole->Profile.getValue()) + Sketcher::SketchObject* pcSketch = nullptr; + if (pcHole->Profile.getValue()) { pcSketch = static_cast(pcHole->Profile.getValue()); + } // if abort command deleted the object the sketch is visible again - if (pcSketch && Gui::Application::Instance->getViewProvider(pcSketch)) + if (pcSketch && Gui::Application::Instance->getViewProvider(pcSketch)) { Gui::Application::Instance->getViewProvider(pcSketch)->show(); + } return ViewProvider::onDelete(s); } + +TaskDlgFeatureParameters* ViewProviderHole::getEditDialog() +{ + return new TaskDlgHoleParameters(this); +} diff --git a/src/Mod/PartDesign/Gui/ViewProviderHole.h b/src/Mod/PartDesign/Gui/ViewProviderHole.h index d57cb782c1..a586f0e15d 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderHole.h +++ b/src/Mod/PartDesign/Gui/ViewProviderHole.h @@ -43,8 +43,9 @@ public: std::vector claimChildren()const override; void setupContextMenu(QMenu *menu, QObject *receiver, const char *member) override; bool onDelete(const std::vector &s) override; + protected: - bool setEdit(int ModNum) override; + TaskDlgFeatureParameters* getEditDialog() override; }; diff --git a/src/Mod/PartDesign/Gui/ViewProviderLoft.cpp b/src/Mod/PartDesign/Gui/ViewProviderLoft.cpp index 792492eccb..c072846781 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderLoft.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderLoft.cpp @@ -37,7 +37,7 @@ using namespace PartDesignGui; -PROPERTY_SOURCE(PartDesignGui::ViewProviderLoft,PartDesignGui::ViewProvider) +PROPERTY_SOURCE(PartDesignGui::ViewProviderLoft, PartDesignGui::ViewProvider) ViewProviderLoft::ViewProviderLoft() = default; @@ -64,28 +64,13 @@ std::vector ViewProviderLoft::claimChildren()const void ViewProviderLoft::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { addDefaultAction(menu, QObject::tr("Edit Loft")); - PartDesignGui::ViewProvider::setupContextMenu(menu, receiver, member); -} - -bool ViewProviderLoft::setEdit(int ModNum) -{ - if (ModNum == ViewProvider::Default) - setPreviewDisplayMode(true); - - return ViewProviderAddSub::setEdit(ModNum); + ViewProvider::setupContextMenu(menu, receiver, member); } TaskDlgFeatureParameters* ViewProviderLoft::getEditDialog() { return new TaskDlgLoftParameters(this); } - -void ViewProviderLoft::unsetEdit(int ModNum) { - setPreviewDisplayMode(false); - ViewProviderAddSub::unsetEdit(ModNum); -} - - bool ViewProviderLoft::onDelete(const std::vector & /*s*/) {/* PartDesign::Loft* pcLoft = getObject(); diff --git a/src/Mod/PartDesign/Gui/ViewProviderLoft.h b/src/Mod/PartDesign/Gui/ViewProviderLoft.h index 0f780d6f31..721c5bc605 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderLoft.h +++ b/src/Mod/PartDesign/Gui/ViewProviderLoft.h @@ -24,11 +24,11 @@ #ifndef PARTGUI_ViewProviderLoft_H #define PARTGUI_ViewProviderLoft_H -#include "ViewProviderAddSub.h" +#include "ViewProvider.h" namespace PartDesignGui { -class PartDesignGuiExport ViewProviderLoft : public ViewProviderAddSub +class PartDesignGuiExport ViewProviderLoft : public ViewProvider { PROPERTY_HEADER_WITH_OVERRIDE(PartDesignGui::ViewProviderLoft); @@ -55,8 +55,6 @@ public: protected: QIcon getIcon() const override; - bool setEdit(int ModNum) override; - void unsetEdit(int ModNum) override; TaskDlgFeatureParameters* getEditDialog() override; private: diff --git a/src/Mod/PartDesign/Gui/ViewProviderPipe.cpp b/src/Mod/PartDesign/Gui/ViewProviderPipe.cpp index f8ffd26274..7c4fc665e1 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderPipe.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderPipe.cpp @@ -75,19 +75,6 @@ void ViewProviderPipe::setupContextMenu(QMenu* menu, QObject* receiver, const ch PartDesignGui::ViewProvider::setupContextMenu(menu, receiver, member); } -bool ViewProviderPipe::setEdit(int ModNum) { - if (ModNum == ViewProvider::Default ) - setPreviewDisplayMode(true); - - return PartDesignGui::ViewProvider::setEdit(ModNum); -} - -void ViewProviderPipe::unsetEdit(int ModNum) { - setPreviewDisplayMode(false); - PartDesignGui::ViewProvider::unsetEdit(ModNum); -} - - TaskDlgFeatureParameters* ViewProviderPipe::getEditDialog() { return new TaskDlgPipeParameters(this, false); } diff --git a/src/Mod/PartDesign/Gui/ViewProviderPipe.h b/src/Mod/PartDesign/Gui/ViewProviderPipe.h index 3526668415..d8650b3d0a 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderPipe.h +++ b/src/Mod/PartDesign/Gui/ViewProviderPipe.h @@ -24,11 +24,11 @@ #ifndef PARTGUI_ViewProviderPipe_H #define PARTGUI_ViewProviderPipe_H -#include "ViewProviderAddSub.h" +#include "ViewProvider.h" namespace PartDesignGui { -class PartDesignGuiExport ViewProviderPipe : public ViewProviderAddSub +class PartDesignGuiExport ViewProviderPipe : public ViewProvider { PROPERTY_HEADER_WITH_OVERRIDE(PartDesignGui::ViewProviderPipe); @@ -54,8 +54,6 @@ public: protected: QIcon getIcon() const override; - bool setEdit(int ModNum) override; - void unsetEdit(int ModNum) override; /// Returns a newly created TaskDlgPipeParameters TaskDlgFeatureParameters *getEditDialog() override; diff --git a/src/Mod/PartDesign/Gui/ViewProviderPrimitive.cpp b/src/Mod/PartDesign/Gui/ViewProviderPrimitive.cpp index 5fa3711bb3..e5d6a6fce0 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderPrimitive.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderPrimitive.cpp @@ -46,71 +46,15 @@ ViewProviderPrimitive::ViewProviderPrimitive() = default; ViewProviderPrimitive::~ViewProviderPrimitive() = default; -void ViewProviderPrimitive::attach(App::DocumentObject* obj) { - ViewProviderAddSub::attach(obj); -} - void ViewProviderPrimitive::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { addDefaultAction(menu, QObject::tr("Edit Primitive")); PartDesignGui::ViewProvider::setupContextMenu(menu, receiver, member); } -bool ViewProviderPrimitive::setEdit(int ModNum) +TaskDlgFeatureParameters* ViewProviderPrimitive::getEditDialog() { - if (ModNum == ViewProvider::Default ) { - // When double-clicking on the item for this fillet the - // object unsets and sets its edit mode without closing - // the task panel - Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); - TaskPrimitiveParameters *primitiveDlg = qobject_cast(dlg); - if (dlg && !primitiveDlg) { - QMessageBox msgBox(Gui::getMainWindow()); - msgBox.setText(QObject::tr("A dialog is already open in the task panel")); - msgBox.setInformativeText(QObject::tr("Close this dialog?")); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::Yes); - int ret = msgBox.exec(); - if (ret == QMessageBox::Yes) - Gui::Control().closeDialog(); - else - return false; - } - - // clear the selection (convenience) - Gui::Selection().clearSelection(); - - // always change to PartDesign WB, remember where we come from - oldWb = Gui::Command::assureWorkbench("PartDesignWorkbench"); - - // start the edit dialog - // another pad left open its task panel - if (primitiveDlg) - Gui::Control().showDialog(primitiveDlg); - else - Gui::Control().showDialog(new TaskPrimitiveParameters(this)); - - setPreviewDisplayMode(true); - - return true; - } - else { - return ViewProviderAddSub::setEdit(ModNum); - } -} - -void ViewProviderPrimitive::unsetEdit(int ModNum) -{ - setPreviewDisplayMode(false); - - // Rely on parent class to: - // restitute old workbench (set setEdit above) and close the dialog if exiting editing - PartDesignGui::ViewProvider::unsetEdit(ModNum); - -} - -void ViewProviderPrimitive::updateData(const App::Property* p) { - PartDesignGui::ViewProviderAddSub::updateData(p); + return new TaskDlgPrimitiveParameters(this); } QIcon ViewProviderPrimitive::getIcon() const { diff --git a/src/Mod/PartDesign/Gui/ViewProviderPrimitive.h b/src/Mod/PartDesign/Gui/ViewProviderPrimitive.h index 337d7a2988..906bf74cbc 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderPrimitive.h +++ b/src/Mod/PartDesign/Gui/ViewProviderPrimitive.h @@ -24,11 +24,11 @@ #ifndef PARTGUI_ViewProviderPrimitive_H #define PARTGUI_ViewProviderPrimitive_H -#include "ViewProviderAddSub.h" +#include "ViewProvider.h" namespace PartDesignGui { -class PartDesignGuiExport ViewProviderPrimitive : public ViewProviderAddSub +class PartDesignGuiExport ViewProviderPrimitive : public ViewProvider { PROPERTY_HEADER_WITH_OVERRIDE(PartDesignGui::ViewProviderPrimitive); @@ -38,16 +38,11 @@ public: /// destructor ~ViewProviderPrimitive() override; - void attach(App::DocumentObject*) override; - void updateData(const App::Property*) override; - protected: QIcon getIcon() const override; void setupContextMenu(QMenu* menu, QObject* receiver, const char* member) override; - bool setEdit(int ModNum) override; - void unsetEdit(int ModNum) override; - void updateAddSubShapeIndicator(); + TaskDlgFeatureParameters *getEditDialog() override; std::string displayMode; }; diff --git a/src/Mod/PartDesign/Gui/ViewProviderSketchBased.h b/src/Mod/PartDesign/Gui/ViewProviderSketchBased.h index 5f4fb54c01..d911a00301 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderSketchBased.h +++ b/src/Mod/PartDesign/Gui/ViewProviderSketchBased.h @@ -23,7 +23,7 @@ #ifndef VIEWPROVIDERSKETCHBASED_H_QKP3UG9A #define VIEWPROVIDERSKETCHBASED_H_QKP3UG9A -#include "ViewProviderAddSub.h" +#include "ViewProvider.h" namespace PartDesignGui { @@ -41,7 +41,7 @@ public: ~ViewProviderSketchBased() override; /// grouping handling - std::vector claimChildren()const override; + std::vector claimChildren() const override; bool onDelete(const std::vector &) override;