Skip to content

Commit

Permalink
Merge pull request FreeCAD#12086 from bgbsww/bgbsww-toponamingMakeEle…
Browse files Browse the repository at this point in the history
…mentBoolean

Toponaming: makeElementBoolean
  • Loading branch information
chennes authored Feb 10, 2024
2 parents 0454ec8 + 7ef0b6e commit ad2fb73
Show file tree
Hide file tree
Showing 4 changed files with 459 additions and 93 deletions.
1 change: 1 addition & 0 deletions src/Mod/Part/App/OpenCascadeAll.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@

#include <BRepAlgo.hxx>
#include <BRepAlgo_NormalProjection.hxx>
#include <BRepAlgoAPI_BooleanOperation.hxx>
#include <BRepAlgoAPI_Common.hxx>
#include <BRepAlgoAPI_Cut.hxx>
# include <BRepAlgoAPI_Defeaturing.hxx>
Expand Down
55 changes: 55 additions & 0 deletions src/Mod/Part/App/TopoShape.h
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,61 @@ class PartExport TopoShape: public Data::ComplexGeoData
return TopoShape(Tag, Hasher).makeElementCopy(*this, op, copyGeom, copyMesh);
}


/** Generalized shape making with mapped element name from shape history
*
* @param maker: op code from OpCodes
* @param sources: list of source shapes.
* @param op: optional string to be encoded into topo naming for indicating
* the operation
* @param tol: tolerance option available to some shape making algorithm
*
* @return The original content of this TopoShape is discarded and replaced
* with the new shape built by the shape maker. The function
* returns the TopoShape itself as a self reference so that
* multiple operations can be carried out for the same shape in the
* same line of code.
*/
TopoShape& makeElementBoolean(const char* maker,
const std::vector<TopoShape>& sources,
const char* op = nullptr,
double tol = 0.0);
/** Generalized shape making with mapped element name from shape history
*
* @param maker: op code from TopoShapeOpCodes
* @param source: source shape.
* @param op: optional string to be encoded into topo naming for indicating
* the operation
* @param tol: tolerance option available to some shape making algorithm
*
* @return The original content of this TopoShape is discarded and replaced
* with the new shape built by the shape maker. The function
* returns the TopoShape itself as a self reference so that
* multiple operations can be carried out for the same shape in the
* same line of code.
*/
TopoShape& makeElementBoolean(const char* maker,
const TopoShape& source,
const char* op = nullptr,
double tol = 0.0);

/** Generalized shape making with mapped element name from shape history
*
* @param maker: op code from TopoShapeOpCodes
* @param op: optional string to be encoded into topo naming for indicating
* the operation
* @param tol: tolerance option available to some shape making algorithm
*
* @return Returns the new shape with mappend element name generated from
* shape history using this shape as the source. The shape itself
* is not modified.
*/
TopoShape
makeElementBoolean(const char* maker, const char* op = nullptr, double tol = 0.0) const
{
return TopoShape(0, Hasher).makeElementBoolean(maker, *this, op, tol);
}

/* Make a shell using this shape
* @param silent: whether to throw exception on failure
* @param op: optional string to be encoded into topo naming for indicating
Expand Down
246 changes: 240 additions & 6 deletions src/Mod/Part/App/TopoShapeExpansion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,39 @@
#include <BRepFill_Generator.hxx>
#include <BRepTools.hxx>
#include <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <BRepAlgoAPI_BooleanOperation.hxx>
#include <BRepAlgoAPI_Common.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <BRepAlgoAPI_Fuse.hxx>
#include <BRepAlgoAPI_Section.hxx>
#include <BRepBuilderAPI_Copy.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepOffsetAPI_MakePipe.hxx>
#include <ShapeUpgrade_ShellSewing.hxx>
#include <TopTools_HSequenceOfShape.hxx>
#include <Precision.hxx>
#include <ShapeBuild_ReShape.hxx>
#include <ShapeAnalysis_FreeBounds.hxx>

#include <ShapeFix_Shape.hxx>
#include <ShapeFix_ShapeTolerance.hxx>
#include <ShapeUpgrade_ShellSewing.hxx>
#include <gp_Pln.hxx>

#include <utility>
#include <BRepBuilderAPI_Copy.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <TopTools_HSequenceOfShape.hxx>
#include <ShapeAnalysis_FreeBounds.hxx>

#endif

#if OCC_VERSION_HEX >= 0x070500
# include <OSD_Parallel.hxx>
#endif

#include "TopoShape.h"
#include "TopoShapeOpCode.h"
#include "TopoShapeCache.h"
#include "TopoShapeMapper.h"
#include "FaceMaker.h"

#include "TopoShapeOpCode.h"
#include <App/ElementNamingUtils.h>
#include <BRepLib.hxx>

Expand All @@ -62,6 +75,20 @@ FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT
namespace Part
{

static void expandCompound(const TopoShape& shape, std::vector<TopoShape>& res)
{
if (shape.isNull()) {
FC_THROWM(NullShapeException, "Null input shape");
}
if (shape.getShape().ShapeType() != TopAbs_COMPOUND) {
res.push_back(shape);
return;
}
for (auto& s : shape.getSubTopoShapes()) {
expandCompound(s, res);
}
}

void TopoShape::initCache(int reset) const
{
if (reset > 0 || !_cache || _cache->isTouched(_Shape)) {
Expand Down Expand Up @@ -2362,4 +2389,211 @@ bool TopoShape::fixSolidOrientation()
return false;
}

TopoShape&
TopoShape::makeElementBoolean(const char* maker, const TopoShape& shape, const char* op, double tolerance)
{
return makeElementBoolean(maker, std::vector<TopoShape>(1, shape), op, tolerance);
}


// TODO: Refactor this so that each OpCode type is a separate method to reduce size
TopoShape& TopoShape::makeElementBoolean(const char* maker,
const std::vector<TopoShape>& shapes,
const char* op,
double tolerance)
{
if (!maker) {
FC_THROWM(Base::CADKernelError, "no maker");
}

if (!op) {
op = maker;
}

if (shapes.empty()) {
FC_THROWM(NullShapeException, "Null shape");
}

if (strcmp(maker, Part::OpCodes::Compound) == 0) {
return makeElementCompound(shapes, op, SingleShapeCompoundCreationPolicy::returnShape);
}
else if (boost::starts_with(maker, Part::OpCodes::Face)) {
std::string prefix(Part::OpCodes::Face);
prefix += '.';
const char* face_maker = 0;
if (boost::starts_with(maker, prefix)) {
face_maker = maker + prefix.size();
}
return makeElementFace(shapes, op, face_maker);
}
else if (strcmp(maker, Part::OpCodes::Wire) == 0) {
return makeElementWires(shapes, op);
}
else if (strcmp(maker, Part::OpCodes::Compsolid) == 0) {
BRep_Builder builder;
TopoDS_CompSolid Comp;
builder.MakeCompSolid(Comp);
for (auto& s : shapes) {
if (!s.isNull()) {
builder.Add(Comp, s.getShape());
}
}
setShape(Comp);
mapSubElement(shapes, op);
return *this;
}

if (strcmp(maker, Part::OpCodes::Pipe) == 0) {
if (shapes.size() != 2) {
FC_THROWM(Base::CADKernelError, "Sweep needs a spine and a shape");
}
if (shapes[0].isNull() || shapes[1].isNull()) {
FC_THROWM(Base::CADKernelError, "Cannot sweep with empty spine or empty shape");
}
if (shapes[0].getShape().ShapeType() != TopAbs_WIRE) {
FC_THROWM(Base::CADKernelError, "Spine shape is not a wire");
}
BRepOffsetAPI_MakePipe mkPipe(TopoDS::Wire(shapes[0].getShape()), shapes[1].getShape());
return makeElementShape(mkPipe, shapes, op);
}

if (strcmp(maker, Part::OpCodes::Shell) == 0) {
BRep_Builder builder;
TopoDS_Shell shell;
builder.MakeShell(shell);
for (auto& s : shapes) {
builder.Add(shell, s.getShape());
}
setShape(shell);
mapSubElement(shapes, op);
BRepCheck_Analyzer check(shell);
if (!check.IsValid()) {
ShapeUpgrade_ShellSewing sewShell;
setShape(sewShell.ApplySewing(shell), false);
// TODO: confirm the above won't change OCCT topological naming
}
return *this;
}

bool buildShell = true;

std::vector<TopoShape> _shapes;
if (strcmp(maker, Part::OpCodes::Fuse) == 0) {
for (auto it = shapes.begin(); it != shapes.end(); ++it) {
auto& s = *it;
if (s.isNull()) {
FC_THROWM(NullShapeException, "Null input shape");
}
if (s.shapeType() == TopAbs_COMPOUND) {
if (_shapes.empty()) {
_shapes.insert(_shapes.end(), shapes.begin(), it);
}
expandCompound(s, _shapes);
}
else if (_shapes.size()) {
_shapes.push_back(s);
}
}
}
else if (strcmp(maker, Part::OpCodes::Cut) == 0) {
for (unsigned i = 1; i < shapes.size(); ++i) {
auto& s = shapes[i];
if (s.isNull()) {
FC_THROWM(NullShapeException, "Null input shape");
}
if (s.shapeType() == TopAbs_COMPOUND) {
if (_shapes.empty()) {
_shapes.insert(_shapes.end(), shapes.begin(), shapes.begin() + i);
}
expandCompound(s, _shapes);
}
else if (_shapes.size()) {
_shapes.push_back(s);
}
}
}

if (tolerance > 0.0 && _shapes.empty()) {
_shapes = shapes;
}

const auto& inputs = _shapes.size() ? _shapes : shapes;
if (inputs.empty()) {
FC_THROWM(NullShapeException, "Null input shape");
}
if (inputs.size() == 1) {
*this = inputs[0];
if (shapes.size() == 1) {
// _shapes has fewer items than shapes due to compound expansion.
// Only warn if the caller paseses one shape.
FC_WARN("Boolean operation with only one shape input");
}
return *this;
}

std::unique_ptr<BRepAlgoAPI_BooleanOperation> mk;
if (strcmp(maker, Part::OpCodes::Fuse) == 0) {
mk.reset(new BRepAlgoAPI_Fuse);
}
else if (strcmp(maker, Part::OpCodes::Cut) == 0) {
mk.reset(new BRepAlgoAPI_Cut);
}
else if (strcmp(maker, Part::OpCodes::Common) == 0) {
mk.reset(new BRepAlgoAPI_Common);
}
else if (strcmp(maker, Part::OpCodes::Section) == 0) {
mk.reset(new BRepAlgoAPI_Section);
buildShell = false;
}
else {
FC_THROWM(Base::CADKernelError, "Unknown maker");
}

TopTools_ListOfShape shapeArguments, shapeTools;

int i = -1;
for (const auto& shape : inputs) {
if (shape.isNull()) {
FC_THROWM(NullShapeException, "Null input shape");
}
if (++i == 0) {
shapeArguments.Append(shape.getShape());
}
else if (tolerance > 0.0) {
auto& s = _shapes[i];
// workaround for http://dev.opencascade.org/index.php?q=node/1056#comment-520
s.setShape(BRepBuilderAPI_Copy(s.getShape()).Shape(), false);
shapeTools.Append(s.getShape());
}
else {
shapeTools.Append(shape.getShape());
}
}

#if OCC_VERSION_HEX >= 0x070500
// -1/22/2024 Removing the parameter.
// if (PartParams::getParallelRunThreshold() > 0) {
mk->SetRunParallel(Standard_True);
OSD_Parallel::SetUseOcctThreads(Standard_True);
// }
#else
// 01/22/2024 This will be an extremely rare case, since we don't
// build against OCCT versions this old. Removing the parameter.
mk->SetRunParallel(true);
#endif

mk->SetArguments(shapeArguments);
mk->SetTools(shapeTools);
if (tolerance > 0.0) {
mk->SetFuzzyValue(tolerance);
}
mk->Build();
makeElementShape(*mk, inputs, op);

if (buildShell) {
makeElementShell();
}
return *this;
}

} // namespace Part
Loading

0 comments on commit ad2fb73

Please sign in to comment.