PartDesign: Performance improvements in Transformed (pattern)
- Use fuzzy fuse/cut method with tolerance Precision::Confusion() - Use parallel computation feature - Allow that individual patterned item fail to intersect the baseobject. Issue warning instead of error. This was done for performance reasons but it can also be considered a feature. - Simplify the code a bit - Distinguish between overlapping mode and non-overlapping mode. In non-overlapping mode the tool shapes are compounded instead of fused. For huge benefit in performance.
This commit is contained in:
@@ -36,6 +36,9 @@
|
||||
# include <Bnd_Box.hxx>
|
||||
#endif
|
||||
|
||||
#ifndef FC_DEBUG
|
||||
#include <ctime>
|
||||
#endif
|
||||
|
||||
#include "FeatureTransformed.h"
|
||||
#include "FeatureMultiTransform.h"
|
||||
@@ -198,7 +201,11 @@ short Transformed::mustExecute() const
|
||||
|
||||
App::DocumentObjectExecReturn *Transformed::execute(void)
|
||||
{
|
||||
rejected.clear();
|
||||
|
||||
#ifndef FC_DEBUG
|
||||
std::clock_t start0;
|
||||
start0 = std::clock();
|
||||
#endif
|
||||
|
||||
std::vector<App::DocumentObject*> originals = Originals.getValues();
|
||||
if (originals.empty()) // typically InsideMultiTransform
|
||||
@@ -246,10 +253,6 @@ App::DocumentObjectExecReturn *Transformed::execute(void)
|
||||
supportShape.setTransform(Base::Matrix4D());
|
||||
TopoDS_Shape support = supportShape.getShape();
|
||||
|
||||
typedef std::set<std::vector<gp_Trsf>::const_iterator> trsf_it;
|
||||
typedef std::map<App::DocumentObject*, trsf_it> rej_it_map;
|
||||
rej_it_map nointersect_trsfms;
|
||||
|
||||
// NOTE: It would be possible to build a compound from all original addShapes/subShapes and then
|
||||
// transform the compounds as a whole. But we choose to apply the transformations to each
|
||||
// Original separately. This way it is easier to discover what feature causes a fuse/cut
|
||||
@@ -276,132 +279,93 @@ App::DocumentObjectExecReturn *Transformed::execute(void)
|
||||
else {
|
||||
return new App::DocumentObjectExecReturn("Only additive and subtractive features can be transformed");
|
||||
}
|
||||
TopoDS_Shape origShape = fuseShape.isNull()?cutShape.getShape():fuseShape.getShape();
|
||||
|
||||
// Transform the add/subshape and collect the resulting shapes for overlap testing
|
||||
/*typedef std::vector<std::vector<gp_Trsf>::const_iterator> trsf_it_vec;
|
||||
trsf_it_vec v_transformations;
|
||||
std::vector<TopoDS_Shape> v_transformedShapes;*/
|
||||
TopoDS_Shape current = support;
|
||||
|
||||
BRep_Builder builder;
|
||||
TopoDS_Compound compShape;
|
||||
builder.MakeCompound(compShape);
|
||||
std::vector<TopoDS_Shape> shapes;
|
||||
bool overlapping = false;
|
||||
|
||||
std::vector<gp_Trsf>::const_iterator t = transformations.begin();
|
||||
++t; // Skip first transformation, which is always the identity transformation
|
||||
for (; t != transformations.end(); ++t) {
|
||||
// Make an explicit copy of the shape because the "true" parameter to BRepBuilderAPI_Transform
|
||||
// seems to be pretty broken
|
||||
BRepBuilderAPI_Copy copy(fuseShape.isNull()?cutShape.getShape():fuseShape.getShape());
|
||||
BRepBuilderAPI_Copy copy(origShape);
|
||||
|
||||
shape = copy.Shape();
|
||||
if (shape.IsNull())
|
||||
return new App::DocumentObjectExecReturn("Transformed: Linked shape object is empty");
|
||||
|
||||
BRepBuilderAPI_Transform mkTrf(shape, *t, false); // No need to copy, now
|
||||
if (!mkTrf.IsDone())
|
||||
return new App::DocumentObjectExecReturn("Transformation failed", (*o));
|
||||
shape.Move(*t);
|
||||
shapes.emplace_back(shape);
|
||||
builder.Add(compShape, shape);
|
||||
|
||||
shape = mkTrf.Shape();
|
||||
try {
|
||||
// Intersection checking for additive shape is redundant.
|
||||
// Because according to CheckIntersection() source code, it is
|
||||
// implemented using fusion and counting of the resulting
|
||||
// solid, which will be done in the following modeling step
|
||||
// anyway.
|
||||
//
|
||||
// There is little reason for doing intersection checking on
|
||||
// subtractive shape either, because it does not produce
|
||||
// multiple solids.
|
||||
//
|
||||
// if (!Part::checkIntersection(support, mkTrf.Shape(), false, true)) {
|
||||
overlapping = overlapping || (countSolids(TopoShape(origShape).fuse(shape))==1);
|
||||
|
||||
|
||||
TopoDS_Shape current = support;
|
||||
|
||||
if (!fuseShape.isNull()) {
|
||||
// We cannot wait to fuse a transformation with the support until all the transformations are done,
|
||||
// because the "support" potentially changes with every transformation, basically when checking intersection
|
||||
// above you need:
|
||||
// 1. The original support
|
||||
// 2. Any extra support gained by any previous transformation of any previous feature (multi-feature transform)
|
||||
// 3. Any extra support gained by any previous transformation of this feature (feature multi-trasform)
|
||||
//
|
||||
// Therefore, if the transformation succeeded, then we fuse it with the support now, before checking the intersection
|
||||
// of the next transformation.
|
||||
|
||||
/*v_transformations.push_back(t);
|
||||
v_transformedShapes.push_back(mkTrf.Shape());*/
|
||||
|
||||
// Note: Transformations that do not intersect the support are ignored in the overlap tests
|
||||
|
||||
//insert scheme here.
|
||||
/*TopoDS_Compound compoundTool;
|
||||
std::vector<TopoDS_Shape> individualTools;
|
||||
divideTools(v_transformedShapes, individualTools, compoundTool);*/
|
||||
|
||||
// Fuse/Cut the compounded transformed shapes with the support
|
||||
//TopoDS_Shape result;
|
||||
|
||||
BRepAlgoAPI_Fuse mkFuse(current, shape);
|
||||
if (!mkFuse.IsDone())
|
||||
return new App::DocumentObjectExecReturn("Fusion with support failed", *o);
|
||||
|
||||
if(Part::TopoShape(current).countSubShapes(TopAbs_SOLID)
|
||||
!= Part::TopoShape(mkFuse.Shape()).countSubShapes(TopAbs_SOLID))
|
||||
{
|
||||
#ifdef FC_DEBUG // do not write this in release mode because a message appears already in the task view
|
||||
Base::Console().Warning("Transformed shape does not intersect support %s: Removed\n", (*o)->getNameInDocument());
|
||||
#endif
|
||||
nointersect_trsfms[*o].insert(t);
|
||||
continue;
|
||||
}
|
||||
// we have to get the solids (fuse sometimes creates compounds)
|
||||
current = this->getSolid(mkFuse.Shape());
|
||||
// lets check if the result is a solid
|
||||
if (current.IsNull())
|
||||
return new App::DocumentObjectExecReturn("Resulting shape is not a solid", *o);
|
||||
|
||||
if (!cutShape.isNull()) {
|
||||
BRepBuilderAPI_Copy copy(cutShape.getShape());
|
||||
shape = copy.Shape();
|
||||
if (shape.IsNull())
|
||||
return new App::DocumentObjectExecReturn("Transformed: Linked shape object is empty");
|
||||
|
||||
BRepBuilderAPI_Transform mkTrf(shape, *t, false); // No need to copy, now
|
||||
if (!mkTrf.IsDone())
|
||||
return new App::DocumentObjectExecReturn("Transformation failed", (*o));
|
||||
shape = mkTrf.Shape();
|
||||
}
|
||||
}
|
||||
if (!cutShape.isNull()) {
|
||||
BRepAlgoAPI_Cut mkCut(current, shape);
|
||||
if (!mkCut.IsDone())
|
||||
return new App::DocumentObjectExecReturn("Cut out of support failed", *o);
|
||||
current = mkCut.Shape();
|
||||
}
|
||||
support = current; // Use result of this operation for fuse/cut of next original
|
||||
} catch (Standard_Failure& e) {
|
||||
// Note: Ignoring this failure is probably pointless because if the intersection check fails, the later
|
||||
// fuse operation of the transformation result will also fail
|
||||
|
||||
std::string msg("Transformation: Intersection check failed");
|
||||
if (e.GetMessageString() != NULL)
|
||||
msg += std::string(": '") + e.GetMessageString() + "'";
|
||||
return new App::DocumentObjectExecReturn(msg.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
support = refineShapeIfActive(support);
|
||||
|
||||
for (rej_it_map::const_iterator it = nointersect_trsfms.begin(); it != nointersect_trsfms.end(); ++it)
|
||||
for (trsf_it::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2)
|
||||
rejected[it->first].push_back(**it2);
|
||||
TopoDS_Shape toolShape;
|
||||
|
||||
if (overlapping) {
|
||||
|
||||
#ifndef FC_DEBUG
|
||||
Base::Console().Message("Transformed: Overlapping feature mode (fusing tool shapes)\n");
|
||||
#endif
|
||||
|
||||
toolShape = TopoShape(origShape).fuse(shapes, Precision::Confusion());
|
||||
} else {
|
||||
|
||||
#ifndef FC_DEBUG
|
||||
Base::Console().Message("Transformed: Non-Overlapping feature mode (compound of tool shapes)\n");
|
||||
#endif
|
||||
|
||||
toolShape = compShape;
|
||||
}
|
||||
|
||||
if (!fuseShape.isNull()) {
|
||||
std::unique_ptr<BRepAlgoAPI_BooleanOperation> mkBool(new BRepAlgoAPI_Fuse(current, toolShape));
|
||||
if (!mkBool->IsDone()) {
|
||||
std::stringstream error;
|
||||
error << "Boolean operation failed";
|
||||
return new App::DocumentObjectExecReturn(error.str());
|
||||
}
|
||||
current = mkBool->Shape();
|
||||
} else {
|
||||
std::unique_ptr<BRepAlgoAPI_BooleanOperation> mkBool(new BRepAlgoAPI_Cut(current, toolShape));
|
||||
if (!mkBool->IsDone()) {
|
||||
std::stringstream error;
|
||||
error << "Boolean operation failed";
|
||||
return new App::DocumentObjectExecReturn(error.str());
|
||||
}
|
||||
current = mkBool->Shape();
|
||||
}
|
||||
try {} catch (Standard_Failure& e) {
|
||||
// Note: Ignoring this failure is probably pointless because if the intersection check fails, the later
|
||||
// fuse operation of the transformation result will also fail
|
||||
|
||||
std::string msg("Transformation: Intersection check failed");
|
||||
if (e.GetMessageString() != NULL)
|
||||
msg += std::string(": '") + e.GetMessageString() + "'";
|
||||
return new App::DocumentObjectExecReturn(msg.c_str());
|
||||
}
|
||||
|
||||
support = current; // Use result of this operation for fuse/cut of next original
|
||||
}
|
||||
|
||||
support = refineShapeIfActive(support);
|
||||
|
||||
int solidCount = countSolids(support);
|
||||
if (solidCount > 1) {
|
||||
return new App::DocumentObjectExecReturn("Transformed: Result has multiple solids. This is not supported at this time.");
|
||||
Base::Console().Warning("Transformed: Result has multiple solids. Only keeping the first.\n");
|
||||
}
|
||||
|
||||
this->Shape.setValue(getSolid(support));
|
||||
this->Shape.setValue(getSolid(support)); // picking the first solid
|
||||
|
||||
if (rejected.size() > 0) {
|
||||
return new App::DocumentObjectExecReturn("Transformation failed");
|
||||
}
|
||||
#ifndef FC_DEBUG
|
||||
Base::Console().Message("Transformed: Elapsed CPU time: %f s\n", (std::clock() - start0 ) / (double)(CLOCKS_PER_SEC));
|
||||
#endif
|
||||
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
@@ -412,6 +376,9 @@ TopoDS_Shape Transformed::refineShapeIfActive(const TopoDS_Shape& oldShape) cons
|
||||
try {
|
||||
Part::BRepBuilderAPI_RefineModel mkRefine(oldShape);
|
||||
TopoDS_Shape resShape = mkRefine.Shape();
|
||||
if (!TopoShape(resShape).isClosed()) {
|
||||
return oldShape;
|
||||
}
|
||||
return resShape;
|
||||
}
|
||||
catch (Standard_Failure&) {
|
||||
|
||||
Reference in New Issue
Block a user