PartDesign: Add transparent previews
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -225,6 +225,8 @@ SET(Features_SRCS
|
||||
DatumFeature.h
|
||||
AttachExtension.h
|
||||
AttachExtension.cpp
|
||||
PreviewExtension.cpp
|
||||
PreviewExtension.h
|
||||
PrismExtension.cpp
|
||||
PrismExtension.h
|
||||
VectorAdapter.cpp
|
||||
|
||||
67
src/Mod/Part/App/PreviewExtension.cpp
Normal file
67
src/Mod/Part/App/PreviewExtension.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 Kacper Donat <kacper@kadet.net> *
|
||||
* *
|
||||
* 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 *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#include "PreviewExtension.h"
|
||||
|
||||
#include <App/DocumentObject.h>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
82
src/Mod/Part/App/PreviewExtension.h
Normal file
82
src/Mod/Part/App/PreviewExtension.h
Normal file
@@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 Kacper Donat <kacper@kadet.net> *
|
||||
* *
|
||||
* 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 *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef PART_PREVIEWEXTENSION_H
|
||||
#define PART_PREVIEWEXTENSION_H
|
||||
|
||||
#include "PropertyTopoShape.h"
|
||||
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/DocumentObjectExtension.h>
|
||||
#include <App/ExtensionPython.h>
|
||||
|
||||
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<typename ExtensionT>
|
||||
class PreviewExtensionPythonT: public ExtensionT
|
||||
{
|
||||
|
||||
public:
|
||||
PreviewExtensionPythonT() = default;
|
||||
~PreviewExtensionPythonT() override = default;
|
||||
};
|
||||
|
||||
using PreviewExtensionPython = App::ExtensionPythonT<PreviewExtensionPythonT<PreviewExtension>>;
|
||||
|
||||
} // namespace Part
|
||||
|
||||
#endif // PART_PREVIEWEXTENSION_H
|
||||
@@ -71,7 +71,11 @@
|
||||
|
||||
#include "Tools.h"
|
||||
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <TopoShape.h>
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@
|
||||
#include <vector>
|
||||
|
||||
|
||||
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
|
||||
|
||||
@@ -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<std::string> buildShapeEnumVector()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -162,6 +162,8 @@ SET(PartGui_SRCS
|
||||
ViewProvider.h
|
||||
ViewProviderAttachExtension.h
|
||||
ViewProviderAttachExtension.cpp
|
||||
ViewProviderPreviewExtension.h
|
||||
ViewProviderPreviewExtension.cpp
|
||||
ViewProviderDatum.cpp
|
||||
ViewProviderDatum.h
|
||||
ViewProviderExt.cpp
|
||||
|
||||
@@ -564,7 +564,7 @@ std::vector<Base::Vector3d> ViewProviderPartExt::getModelPoints(const SoPickedPo
|
||||
try {
|
||||
std::vector<Base::Vector3d> 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<int> 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<int, std::vector<int32_t>> lineSetMap;
|
||||
std::set<int> edgeIdxSet;
|
||||
std::vector<int32_t> 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<int32_t> 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<int32_t>::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<int> 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<int, std::vector<int32_t> > lineSetMap;
|
||||
std::set<int> edgeIdxSet;
|
||||
std::vector<int32_t> 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<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<int32_t> 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<int32_t>::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());
|
||||
|
||||
@@ -123,6 +123,10 @@ public:
|
||||
std::vector<Base::Vector3d> 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;
|
||||
|
||||
197
src/Mod/Part/Gui/ViewProviderPreviewExtension.cpp
Normal file
197
src/Mod/Part/Gui/ViewProviderPreviewExtension.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 Kacper Donat <kacper@kadet.net> *
|
||||
* *
|
||||
* 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 *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <Inventor/nodes/SoDrawStyle.h>
|
||||
# include <Inventor/nodes/SoLightModel.h>
|
||||
# include <Inventor/nodes/SoMaterial.h>
|
||||
# include <Inventor/nodes/SoPickStyle.h>
|
||||
# include <Inventor/nodes/SoPolygonOffset.h>
|
||||
#endif
|
||||
|
||||
#include "ViewProviderPreviewExtension.h"
|
||||
#include "ViewProviderExt.h"
|
||||
|
||||
#include <Gui/Utilities.h>
|
||||
#include <Gui/Inventor/So3DAnnotation.h>
|
||||
#include <Mod/Part/App/Tools.h>
|
||||
|
||||
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::PropertyType>(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<SbColor>(PreviewColor.getValue()));
|
||||
}
|
||||
|
||||
ViewProviderExtension::extensionOnChanged(prop);
|
||||
}
|
||||
|
||||
void ViewProviderPreviewExtension::updatePreviewShape()
|
||||
{
|
||||
auto vp = freecad_cast<ViewProviderPartExt*>(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<PartGui::ViewProviderPreviewExtension>;
|
||||
}
|
||||
110
src/Mod/Part/Gui/ViewProviderPreviewExtension.h
Normal file
110
src/Mod/Part/Gui/ViewProviderPreviewExtension.h
Normal file
@@ -0,0 +1,110 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 Kacper Donat <kacper@kadet.net> *
|
||||
* *
|
||||
* 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 *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef PARTGUI_VIEWPROVIDERPREVIEWEXTENSION_H
|
||||
#define PARTGUI_VIEWPROVIDERPREVIEWEXTENSION_H
|
||||
|
||||
#include "SoBrepEdgeSet.h"
|
||||
#include "SoBrepFaceSet.h"
|
||||
#include "SoBrepPointSet.h"
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
#include <Inventor/nodes/SoSubNode.h>
|
||||
#include <Inventor/nodes/SoCoordinate3.h>
|
||||
#include <Inventor/nodes/SoNormal.h>
|
||||
#include <Inventor/nodes/SoSeparator.h>
|
||||
#include <Inventor/fields/SoSFColor.h>
|
||||
#include <Inventor/fields/SoSFFloat.h>
|
||||
|
||||
#include <App/PropertyStandard.h>
|
||||
#include <Gui/ViewProvider.h>
|
||||
|
||||
#include <Gui/ViewProviderExtension.h>
|
||||
#include <Gui/ViewProviderExtensionPython.h>
|
||||
#include <Mod/Part/App/TopoShape.h>
|
||||
|
||||
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<SoSeparator> pcPreviewRoot;
|
||||
Gui::CoinPtr<SoPreviewShape> pcPreviewShape;
|
||||
|
||||
private:
|
||||
bool _isPreviewEnabled {false};
|
||||
};
|
||||
|
||||
using ViewProviderPreviewExtensionPython = Gui::ViewProviderExtensionPythonT<ViewProviderPreviewExtension>;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // PARTGUI_VIEWPROVIDERPREVIEWEXTENSION_H
|
||||
@@ -44,6 +44,8 @@
|
||||
#include "Body.h"
|
||||
#include "ShapeBinder.h"
|
||||
|
||||
#include <BRep_Builder.hxx>
|
||||
|
||||
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<int>& faces,
|
||||
std::vector<int>& edges,
|
||||
std::vector<int>& vertices) const
|
||||
{
|
||||
static const auto addAllSubShapesToSet = [](
|
||||
const Part::TopoShape& shape,
|
||||
const Part::TopoShape& face,
|
||||
TopAbs_ShapeEnum type,
|
||||
std::set<int>& 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<int> edgeSet;
|
||||
std::set<int> 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())){
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <App/PropertyStandard.h>
|
||||
#include <App/SuppressibleExtension.h>
|
||||
#include <Mod/Part/App/PartFeature.h>
|
||||
#include <Mod/Part/App/PreviewExtension.h>
|
||||
#include <Mod/PartDesign/PartDesignGlobal.h>
|
||||
|
||||
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<int>& faces,
|
||||
std::vector<int>& edges,
|
||||
std::vector<int>& vertices) const;
|
||||
|
||||
virtual void updatePreviewShape();
|
||||
|
||||
PyObject* getPyObject() override;
|
||||
|
||||
const char* getViewProviderName() const override {
|
||||
|
||||
@@ -28,10 +28,13 @@
|
||||
|
||||
#include <App/FeaturePythonPyImp.h>
|
||||
#include <Mod/Part/App/modelRefine.h>
|
||||
#include <Mod/Part/App/TopoShapeOpCode.h>
|
||||
|
||||
#include "FeatureAddSub.h"
|
||||
#include "FeaturePy.h"
|
||||
|
||||
#include <Mod/Part/App/Tools.h>
|
||||
|
||||
|
||||
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)
|
||||
|
||||
@@ -26,12 +26,15 @@
|
||||
|
||||
#include "FeatureRefine.h"
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
/// 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;
|
||||
|
||||
|
||||
|
||||
@@ -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<Part::TopoShape> 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<DressUp*>(base)) {
|
||||
for (Feature* current = this;; current = static_cast<DressUp*>(base)) {
|
||||
base = freecad_cast<FeatureAddSub*>(current->getBaseObject(true));
|
||||
if(!base)
|
||||
if (!base) {
|
||||
FC_THROWM(Base::CADKernelError,
|
||||
"Cannot find additive or subtractive support for " << getFullName());
|
||||
if(!base->isDerivedFrom<DressUp>())
|
||||
"Cannot find additive or subtractive support for "
|
||||
<< getFullName());
|
||||
}
|
||||
if (!base->isDerivedFrom<DressUp>()) {
|
||||
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<Feature*>(BaseFeature.getValue());
|
||||
|
||||
if (!baseFeature || baseFeature->Shape.getShape().isNull()) {
|
||||
PreviewShape.setValue(Shape.getShape());
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<int> 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
|
||||
|
||||
@@ -65,6 +65,7 @@ public:
|
||||
|
||||
std::vector<TopoShape> getFaces(const TopoShape &shape);
|
||||
void getAddSubShape(Part::TopoShape &addShape, Part::TopoShape &subShape) override;
|
||||
void updatePreviewShape() override;
|
||||
|
||||
protected:
|
||||
void onChanged(const App::Property* prop) override;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -109,8 +109,6 @@ SET(PartDesignGuiViewProvider_SRCS
|
||||
ViewProviderShapeBinder.cpp
|
||||
ViewProviderBoolean.cpp
|
||||
ViewProviderBoolean.h
|
||||
ViewProviderAddSub.cpp
|
||||
ViewProviderAddSub.h
|
||||
ViewProviderPrimitive.h
|
||||
ViewProviderPrimitive.cpp
|
||||
ViewProviderPipe.h
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<std::string> 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<typename T = App::DocumentObject> T* getObject() const
|
||||
{
|
||||
static_assert(std::is_base_of<App::DocumentObject, T>::value, "Wrong template argument");
|
||||
|
||||
if (!DressUpView.expired()) {
|
||||
return DressUpView->getObject<T>();
|
||||
}
|
||||
|
||||
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<ViewProviderDressUp> DressUpView;
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include <Gui/Command.h>
|
||||
#include <Gui/Tools.h>
|
||||
#include <Mod/PartDesign/App/FeatureExtrude.h>
|
||||
#include <Mod/Part/Gui/ReferenceHighlighter.h>
|
||||
|
||||
#include "ui_TaskPadPocketParameters.h"
|
||||
#include "TaskExtrudeParameters.h"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -286,7 +286,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxUpdateView">
|
||||
<property name="text">
|
||||
<string>Update view</string>
|
||||
<string>Recompute on change</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxUpdateView">
|
||||
<property name="text">
|
||||
<string>Update view</string>
|
||||
<string>Recompute on change</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -468,7 +468,7 @@ measured along the specified direction</string>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxUpdateView">
|
||||
<property name="text">
|
||||
<string>Update view</string>
|
||||
<string>Recompute on change</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -189,7 +189,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxUpdateView">
|
||||
<property name="text">
|
||||
<string>Update view</string>
|
||||
<string>Recompute on change</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -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<ViewProvider>()->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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<std::string>& subValues);
|
||||
|
||||
private:
|
||||
Gui::ViewProvider* previouslyVisibleViewProvider {nullptr};
|
||||
};
|
||||
|
||||
class TaskDlgSketchBasedParameters : public PartDesignGui::TaskDlgFeatureParameters
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxUpdateView">
|
||||
<property name="text">
|
||||
<string>Update view</string>
|
||||
<string>Recompute on change</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -26,8 +26,10 @@
|
||||
#ifndef _PreComp_
|
||||
# include <QMessageBox>
|
||||
# include <QAction>
|
||||
# include <QApplication>
|
||||
# include <QMenu>
|
||||
# include <Inventor/nodes/SoSeparator.h>
|
||||
# include <Inventor/nodes/SoPickStyle.h>
|
||||
# include <BRep_Builder.hxx>
|
||||
#endif
|
||||
|
||||
#include <Base/Exception.h>
|
||||
@@ -37,15 +39,23 @@
|
||||
#include <Gui/CommandT.h>
|
||||
#include <Gui/Control.h>
|
||||
#include <Gui/Document.h>
|
||||
#include <Gui/Selection/SoFCUnifiedSelection.h>
|
||||
#include <Gui/Inventor/So3DAnnotation.h>
|
||||
#include <Gui/MainWindow.h>
|
||||
#include <Gui/Utilities.h>
|
||||
#include <Mod/PartDesign/App/Body.h>
|
||||
#include <Mod/PartDesign/App/Feature.h>
|
||||
#include <Mod/PartDesign/App/FeatureAddSub.h>
|
||||
#include <Mod/Part/Gui/ViewProvider.h>
|
||||
#include <Mod/Part/Gui/ViewProviderExt.h>
|
||||
#include <Mod/Part/Gui/SoBrepEdgeSet.h>
|
||||
#include <Mod/Part/Gui/ViewProviderPreviewExtension.h>
|
||||
|
||||
#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<PartDesign::FeatureAddSub>()) {
|
||||
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<Part::PropertyPartShape>() &&
|
||||
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<PartDesign::FeatureAddSub>()) {
|
||||
// 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<std::string>&)
|
||||
//
|
||||
// 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<Part::PreviewExtension>(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>()};
|
||||
PartDesign::Feature* baseFeature { nullptr };
|
||||
|
||||
ViewProvider* baseFeatureViewProvider { nullptr };
|
||||
|
||||
if (!feature) {
|
||||
return;
|
||||
}
|
||||
|
||||
baseFeature = dynamic_cast<PartDesign::Feature*>(feature->BaseFeature.getValue());
|
||||
if (baseFeature) {
|
||||
baseFeatureViewProvider = freecad_cast<ViewProvider*>(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<App::Property*> props;
|
||||
@@ -331,8 +430,6 @@ ViewProviderBody* ViewProvider::getBodyViewProvider() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace Gui {
|
||||
/// @cond DOXERR
|
||||
PROPERTY_SOURCE_TEMPLATE(PartDesignGui::ViewProviderPython, PartDesignGui::ViewProvider)
|
||||
|
||||
@@ -24,12 +24,16 @@
|
||||
#ifndef PARTGUI_ViewProvider_H
|
||||
#define PARTGUI_ViewProvider_H
|
||||
|
||||
#include <Mod/Part/Gui/ViewProvider.h>
|
||||
#include "ViewProviderBody.h"
|
||||
#include <App/DocumentObject.h>
|
||||
#include <Gui/ViewProviderFeaturePython.h>
|
||||
#include "Gui/ViewProviderSuppressibleExtension.h"
|
||||
|
||||
#include <Gui/ViewProviderSuppressibleExtension.h>
|
||||
#include <Mod/Part/Gui/ViewProvider.h>
|
||||
#include <Mod/Part/Gui/ViewProviderAttachExtension.h>
|
||||
#include <Mod/Part/Gui/ViewProviderPreviewExtension.h>
|
||||
#include <Mod/PartDesign/App/Feature.h>
|
||||
|
||||
#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<std::string> &) override;
|
||||
|
||||
@@ -92,7 +108,11 @@ protected:
|
||||
|
||||
std::string oldWb;
|
||||
ViewProvider* previouslyShownViewProvider { nullptr };
|
||||
|
||||
bool isSetTipIcon { false };
|
||||
|
||||
private:
|
||||
Gui::CoinPtr<PartGui::SoPreviewShape> pcToolPreview;
|
||||
};
|
||||
|
||||
using ViewProviderPython = Gui::ViewProviderFeaturePythonT<ViewProvider>;
|
||||
|
||||
@@ -1,261 +0,0 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2015 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
* *
|
||||
* 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 <Inventor/nodes/SoSeparator.h>
|
||||
# include <Inventor/nodes/SoSwitch.h>
|
||||
# include <Inventor/nodes/SoCoordinate3.h>
|
||||
# include <Inventor/nodes/SoNormal.h>
|
||||
# include <Inventor/nodes/SoMaterial.h>
|
||||
# include <Inventor/nodes/SoPickStyle.h>
|
||||
# include <Bnd_Box.hxx>
|
||||
# include <BRep_Tool.hxx>
|
||||
# include <BRepBndLib.hxx>
|
||||
# include <BRepMesh_IncrementalMesh.hxx>
|
||||
# include <Standard_Version.hxx>
|
||||
# include <TopExp_Explorer.hxx>
|
||||
# include <TopoDS.hxx>
|
||||
#endif
|
||||
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Gui/Application.h>
|
||||
#include <Mod/Part/App/Tools.h>
|
||||
#include <Mod/Part/Gui/SoBrepFaceSet.h>
|
||||
#include <Mod/PartDesign/App/FeatureAddSub.h>
|
||||
|
||||
#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<PartDesign::FeatureAddSub*>(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<PartDesign::FeatureAddSub>()->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<gp_Pnt> points;
|
||||
std::vector<Poly_Triangle> facets;
|
||||
Part::Tools::getTriangulation(actFace, points, facets);
|
||||
|
||||
// get normal per vertex
|
||||
std::vector<gp_Vec> 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<PartDesign::Feature>()->BaseFeature.getValue();
|
||||
if (obj)
|
||||
static_cast<PartDesignGui::ViewProvider*>(Gui::Application::Instance->getViewProvider(obj))->makeTemporaryVisible(onoff);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2015 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
* *
|
||||
* 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 <Mod/Part/Gui/SoBrepFaceSet.h>
|
||||
|
||||
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
|
||||
@@ -39,11 +39,23 @@
|
||||
#include "ViewProviderDressUp.h"
|
||||
#include "TaskDressUpParameters.h"
|
||||
|
||||
#include <Gui/Utilities.h>
|
||||
|
||||
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<SbColor>(error ? red : PreviewColor.getValue());
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<PartDesign::Helix>();
|
||||
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<App::DocumentObject*> ViewProviderHelix::claimChildren() const {
|
||||
std::vector<App::DocumentObject*> temp;
|
||||
App::DocumentObject* sketch = getObject<PartDesign::ProfileBased>()->Profile.getValue();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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<TaskDlgHoleParameters *>(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<std::string> &s)
|
||||
bool ViewProviderHole::onDelete(const std::vector<std::string>& s)
|
||||
{
|
||||
// get the Sketch
|
||||
PartDesign::Hole* pcHole = getObject<PartDesign::Hole>();
|
||||
Sketcher::SketchObject *pcSketch = nullptr;
|
||||
if (pcHole->Profile.getValue())
|
||||
Sketcher::SketchObject* pcSketch = nullptr;
|
||||
if (pcHole->Profile.getValue()) {
|
||||
pcSketch = static_cast<Sketcher::SketchObject*>(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);
|
||||
}
|
||||
|
||||
@@ -43,8 +43,9 @@ public:
|
||||
std::vector<App::DocumentObject*> claimChildren()const override;
|
||||
void setupContextMenu(QMenu *menu, QObject *receiver, const char *member) override;
|
||||
bool onDelete(const std::vector<std::string> &s) override;
|
||||
|
||||
protected:
|
||||
bool setEdit(int ModNum) override;
|
||||
TaskDlgFeatureParameters* getEditDialog() override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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<App::DocumentObject*> 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<std::string> & /*s*/)
|
||||
{/*
|
||||
PartDesign::Loft* pcLoft = getObject<PartDesign::Loft>();
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<TaskPrimitiveParameters *>(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 {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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<App::DocumentObject*> claimChildren()const override;
|
||||
std::vector<App::DocumentObject*> claimChildren() const override;
|
||||
|
||||
bool onDelete(const std::vector<std::string> &) override;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user