Merge pull request #12405 from bgbsww/bgbsww-toponamingMakeElementDraft

Toponaming/Part: Transfer in MakeElementDraft
This commit is contained in:
Chris Hennes
2024-02-14 21:10:56 -06:00
committed by GitHub
5 changed files with 198 additions and 27 deletions

View File

@@ -997,6 +997,43 @@ public:
{
return TopoShape(0, Hasher).makeElementBoolean(maker, *this, op, tol);
}
/* Make draft shape
*
* @param source: the source shape
* @param faces: the faces of the source shape to make draft faces
* @param pullDirection: the pulling direction for making the draft
* @param angle: the angle of the draft
* @param neutralPlane: the neutral plane used as a reference to decide pulling direction
* @param retry: whether to keep going by skipping faces that failed to create draft
* @param op: optional string to be encoded into topo naming for indicating
* the operation
*
* @return The original content of this TopoShape is discarded and replaced
* with the new shape. 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 &makeElementDraft(const TopoShape &source, const std::vector<TopoShape> &faces,
const gp_Dir &pullDirection, double angle, const gp_Pln &neutralPlane,
bool retry=true, const char *op=nullptr);
/* Make draft shape
*
* @param source: the source shape
* @param faces: the faces of the source shape to make draft faces
* @param pullDirection: the pulling direction for making the draft
* @param angle: the angle of the draft
* @param neutralPlane: the neutral plane used as a reference to decide pulling direction
* @param retry: whether to keep going by skipping faces that failed to create draft
* @param op: optional string to be encoded into topo naming for indicating
* the operation
*
* @return Return the new shape. The TopoShape itself is not modified.
*/
TopoShape makeElementDraft(const std::vector<TopoShape> &faces,
const gp_Dir &pullDirection, double angle, const gp_Pln &neutralPlane,
bool retry=true, const char *op=nullptr) const {
return TopoShape(0,Hasher).makeElementDraft(*this,faces,pullDirection,angle,neutralPlane,retry,op);
}
/* Make a shell using this shape
* @param silent: whether to throw exception on failure

View File

@@ -41,6 +41,8 @@
#include <BRepAlgoAPI_Section.hxx>
#include <BRepBuilderAPI_Copy.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepLib.hxx>
#include <BRepOffsetAPI_DraftAngle.hxx>
#include <BRepOffsetAPI_MakePipe.hxx>
#include <ShapeUpgrade_ShellSewing.hxx>
#include <TopTools_HSequenceOfShape.hxx>
@@ -57,7 +59,7 @@
#endif
#if OCC_VERSION_HEX >= 0x070500
# include <OSD_Parallel.hxx>
#include <OSD_Parallel.hxx>
#endif
#include "modelRefine.h"
@@ -68,7 +70,6 @@
#include "FaceMaker.h"
#include <App/ElementNamingUtils.h>
#include <BRepLib.hxx>
FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT
@@ -600,12 +601,11 @@ struct NameKey
long tag = 0;
int shapetype = 0;
NameKey()
= default;
explicit NameKey(Data::MappedName n)
NameKey() = default;
explicit NameKey(Data::MappedName n)
: name(std::move(n))
{}
NameKey(int type, Data::MappedName n)
NameKey(int type, Data::MappedName n)
: name(std::move(n))
{
// Order the shape type from vertex < edge < face < other. We'll rely
@@ -795,9 +795,9 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape,
// First, collect names from other shapes that generates or modifies the
// new shape
for (auto& pinfo : infos) { // Walk Vertexes, then Edges, then Faces
for (auto& pinfo : infos) { // Walk Vertexes, then Edges, then Faces
auto& info = *pinfo;
for (const auto & incomingShape : shapes) {
for (const auto& incomingShape : shapes) {
if (!canMapElement(incomingShape)) {
continue;
}
@@ -810,7 +810,8 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape,
const auto& otherElement = otherMap.find(incomingShape._Shape, i);
// Find all new objects that are a modification of the old object
Data::ElementIDRefs sids;
NameKey key(info.type,
NameKey key(
info.type,
incomingShape.getMappedName(Data::IndexedName::fromConst(info.shapetype, i),
true,
&sids));
@@ -850,7 +851,8 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape,
continue;
}
Data::IndexedName element = Data::IndexedName::fromConst(newInfo.shapetype, newShapeIndex);
Data::IndexedName element =
Data::IndexedName::fromConst(newInfo.shapetype, newShapeIndex);
if (getMappedName(element)) {
continue;
}
@@ -1162,11 +1164,13 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape,
TopTools_IndexedMapOfShape submap;
TopExp::MapShapes(info.find(elementCounter), next.type, submap);
for (int submapIndex = 1, infoCounter = 1; submapIndex <= submap.Extent(); ++submapIndex) {
for (int submapIndex = 1, infoCounter = 1; submapIndex <= submap.Extent();
++submapIndex) {
ss.str("");
int elementIndex = next.find(submap(submapIndex));
assert(elementIndex);
Data::IndexedName indexedName = Data::IndexedName::fromConst(next.shapetype, elementIndex);
Data::IndexedName indexedName =
Data::IndexedName::fromConst(next.shapetype, elementIndex);
if (getMappedName(indexedName)) {
continue;
}
@@ -1200,7 +1204,7 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape,
bool hasUnnamed = false;
for (size_t ifo = 1; ifo < infos.size(); ++ifo) {
auto& info = *infos.at(ifo);
auto& prev = *infos.at(ifo-1);
auto& prev = *infos.at(ifo - 1);
for (int i = 1; i <= info.count(); ++i) {
Data::IndexedName element = Data::IndexedName::fromConst(info.shapetype, i);
if (getMappedName(element)) {
@@ -1219,7 +1223,8 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape,
for (; xp.More(); xp.Next()) {
int previousElementIndex = prev.find(xp.Current());
assert(previousElementIndex);
Data::IndexedName prevElement = Data::IndexedName::fromConst(prev.shapetype, previousElementIndex);
Data::IndexedName prevElement =
Data::IndexedName::fromConst(prev.shapetype, previousElementIndex);
if (!delayed && (newNames.count(prevElement) != 0U)) {
names.clear();
break;
@@ -1807,7 +1812,7 @@ struct MapperThruSections: MapperMaker
const std::vector<TopoDS_Shape>& generated(const TopoDS_Shape& s) const override
{
MapperMaker::generated(s);
if ( ! _res.empty()) {
if (!_res.empty()) {
return _res;
}
try {
@@ -1896,6 +1901,54 @@ TopoShape& TopoShape::makeElementShape(BRepPrimAPI_MakeHalfSpace& mkShape,
return makeShapeWithElementMap(mkShape.Solid(), MapperMaker(mkShape), {source}, op);
}
TopoShape& TopoShape::makeElementDraft(const TopoShape& shape,
const std::vector<TopoShape>& _faces,
const gp_Dir& pullDirection,
double angle,
const gp_Pln& neutralPlane,
bool retry,
const char* op)
{
if (!op) {
op = Part::OpCodes::Draft;
}
if (shape.isNull()) {
FC_THROWM(NullShapeException, "Null shape");
}
std::vector<TopoShape> faces(_faces);
bool done = true;
BRepOffsetAPI_DraftAngle mkDraft;
do {
if (faces.empty()) {
FC_THROWM(Base::CADKernelError, "no faces can be used");
}
mkDraft.Init(shape.getShape());
done = true;
for (auto it = faces.begin(); it != faces.end(); ++it) {
// TODO: What is the flag for?
mkDraft.Add(TopoDS::Face(it->getShape()), pullDirection, angle, neutralPlane);
if (!mkDraft.AddDone()) {
// Note: the function ProblematicShape returns the face on which the error occurred
// Note: mkDraft.Remove() stumbles on a bug in Draft_Modification::Remove() and is
// therefore unusable. See
// http://forum.freecadweb.org/viewtopic.php?f=10&t=3209&start=10#p25341 The
// only solution is to discard mkDraft and start over without the current face
// mkDraft.Remove(face);
FC_ERR("Failed to add some face for drafting, skip");
done = false;
faces.erase(it);
break;
}
}
} while (retry && !done);
mkDraft.Build();
return makeElementShape(mkDraft, shape, op);
}
TopoShape& TopoShape::makeElementFace(const TopoShape& shape,
const char* op,
const char* maker,
@@ -1972,18 +2025,20 @@ TopoShape& TopoShape::makeElementFace(const std::vector<TopoShape>& shapes,
return *this;
}
class MyRefineMaker : public BRepBuilderAPI_RefineModel
class MyRefineMaker: public BRepBuilderAPI_RefineModel
{
public:
explicit MyRefineMaker(const TopoDS_Shape &s)
:BRepBuilderAPI_RefineModel(s)
explicit MyRefineMaker(const TopoDS_Shape& s)
: BRepBuilderAPI_RefineModel(s)
{}
void populate(ShapeMapper &mapper)
void populate(ShapeMapper& mapper)
{
for (TopTools_DataMapIteratorOfDataMapOfShapeListOfShape it(this->myModified); it.More(); it.Next())
{
if (it.Key().IsNull()) continue;
for (TopTools_DataMapIteratorOfDataMapOfShapeListOfShape it(this->myModified); it.More();
it.Next()) {
if (it.Key().IsNull()) {
continue;
}
mapper.populate(MappingStatus::Generated, it.Key(), it.Value());
}
}
@@ -2474,8 +2529,10 @@ bool TopoShape::fixSolidOrientation()
return false;
}
TopoShape&
TopoShape::makeElementBoolean(const char* maker, const TopoShape& shape, const char* op, double tolerance)
TopoShape& TopoShape::makeElementBoolean(const char* maker,
const TopoShape& shape,
const char* op,
double tolerance)
{
return makeElementBoolean(maker, std::vector<TopoShape>(1, shape), op, tolerance);
}
@@ -2658,8 +2715,8 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker,
#if OCC_VERSION_HEX >= 0x070500
// -1/22/2024 Removing the parameter.
// if (PartParams::getParallelRunThreshold() > 0) {
mk->SetRunParallel(Standard_True);
OSD_Parallel::SetUseOcctThreads(Standard_True);
mk->SetRunParallel(Standard_True);
OSD_Parallel::SetUseOcctThreads(Standard_True);
// }
#else
// 01/22/2024 This will be an extremely rare case, since we don't

View File

@@ -1,6 +1,7 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
#include <BRepPrimAPI_MakeBox.hxx>
#include <TopExp_Explorer.hxx>
#include "PartTestHelpers.h"
@@ -155,6 +156,28 @@ std::pair<TopoDS_Shape, TopoDS_Shape> CreateTwoCubes()
return {box1, box2};
}
std::pair<TopoShape, TopoShape> CreateTwoTopoShapeCubes()
{
auto [box1, box2] = CreateTwoCubes();
std::vector<TopoShape> vec;
long tag = 1L;
for (TopExp_Explorer exp(box1, TopAbs_FACE); exp.More(); exp.Next()) {
vec.emplace_back(TopoShape(exp.Current(), tag++));
}
TopoShape box1ts;
box1ts.makeElementCompound(vec);
box1ts.Tag = tag++;
vec.clear();
for (TopExp_Explorer exp(box2, TopAbs_FACE); exp.More(); exp.Next()) {
vec.emplace_back(TopoShape(exp.Current(), tag++));
}
TopoShape box2ts;
box2ts.Tag = tag++;
box2ts.makeElementCompound(vec);
return {box1ts, box2ts};
}
} // namespace PartTestHelpers
// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)

View File

@@ -59,4 +59,5 @@ std::map<IndexedName, MappedName> elementMap(const TopoShape& shape);
std::pair<TopoDS_Shape, TopoDS_Shape> CreateTwoCubes();
std::pair<TopoShape, TopoShape> CreateTwoTopoShapeCubes();
} // namespace PartTestHelpers

View File

@@ -13,7 +13,7 @@
#include <BRepFeat_SplitShape.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepAlgoAPI_Fuse.hxx>
#include <GC_MakeCircle.hxx>
#include <gp_Pln.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS_Edge.hxx>
@@ -935,4 +935,57 @@ TEST_F(TopoShapeExpansionTest, makeElementBooleanFuse)
EXPECT_FLOAT_EQ(getVolume(result.getShape()), 1.75);
}
TEST_F(TopoShapeExpansionTest, makeElementDraft)
{ // Draft as in Draft Angle or sloped sides for removing shapes from a mold.
// Arrange
auto [cube1, cube2] = CreateTwoCubes();
TopoShape cube1TS {cube1, 1L};
std::vector<TopoShape> subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE);
std::vector<TopoShape> faces {subShapes[0], subShapes[1], subShapes[2], subShapes[3]};
const gp_Dir pullDirection {0, 0, 1};
double angle {M_PI * 10
/ 8}; // Angle should be between Pi and Pi * 1.5 ( 180 and 270 degrees )
const gp_Pln plane {};
// Act
TopoShape& result = cube1TS.makeElementDraft(cube1TS, faces, pullDirection, angle, plane);
auto elements = elementMap(result);
// Assert
EXPECT_EQ(elements.size(), 26); // Cubes have 6 Faces, 12 Edges, 8 Vertexes
EXPECT_NEAR(getVolume(result.getShape()), 4.3333333333, 1e-06); // Truncated pyramid
}
TEST_F(TopoShapeExpansionTest, makeElementDraftTopoShapes)
{
// Arrange
auto [cube1TS, cube2TS] = CreateTwoTopoShapeCubes();
const gp_Dir pullDirection {0, 0, 1};
double angle {M_PI * 10
/ 8}; // Angle should be between Pi and Pi * 1.5 ( 180 and 270 degrees )
const gp_Pln plane {};
// Act
TopoShape result3 = cube1TS.makeElementDraft(cube1TS.getSubTopoShapes(TopAbs_FACE),
pullDirection,
angle,
plane); // Non Reference call type
TopoShape result2 = cube1TS.makeElementDraft(cube1TS,
cube1TS.getSubTopoShapes(TopAbs_FACE),
pullDirection,
angle,
plane); // Bad use of Reference call
TopoShape& result = cube1TS.makeElementDraft(cube2TS,
cube2TS.getSubTopoShapes(TopAbs_FACE),
pullDirection,
angle,
plane); // Correct usage
auto elements = elementMap((result));
// Assert
EXPECT_TRUE(result.getMappedChildElements().empty());
EXPECT_EQ(elements.size(), 26);
EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1);
EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;:G;DFT;:He:7,F"));
EXPECT_NEAR(getVolume(result.getShape()), 4.3333333333, 1e-06); // Truncated pyramid
EXPECT_EQ(result2.getElementMap().size(), 0); // No element map in non reference call.
EXPECT_EQ(result3.getElementMap().size(), 0); // No element map in non reference call.
}
// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)