Merge pull request #12028 from bgbsww/bgbsww-toponaming-makeElementFace
Toponaming move makEFace as makeElementFace and dependencies
This commit is contained in:
@@ -247,6 +247,10 @@ public:
|
||||
}
|
||||
|
||||
// NOTE: getElementHistory is now in ElementMap
|
||||
long getElementHistory(const MappedName & name,
|
||||
MappedName *original=nullptr, std::vector<MappedName> *history=nullptr) const {
|
||||
return _elementMap->getElementHistory(name, Tag, original, history);
|
||||
};
|
||||
|
||||
void setMappedChildElements(const std::vector<Data::ElementMap::MappedChildElements> & children);
|
||||
std::vector<Data::ElementMap::MappedChildElements> getMappedChildElements() const;
|
||||
|
||||
@@ -20,7 +20,14 @@
|
||||
* *
|
||||
**************************************************************************************************/
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <cstdlib>
|
||||
# include <unordered_set>
|
||||
#endif
|
||||
|
||||
#include "MappedElement.h"
|
||||
|
||||
using namespace Data;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "IndexedName.h"
|
||||
#include "MappedName.h"
|
||||
|
||||
|
||||
namespace App
|
||||
{
|
||||
class DocumentObject;
|
||||
|
||||
@@ -33,7 +33,9 @@
|
||||
#include <memory>
|
||||
|
||||
#include "FaceMaker.h"
|
||||
#include <App/MappedElement.h>
|
||||
#include "TopoShape.h"
|
||||
#include "TopoShapeOpCode.h"
|
||||
|
||||
|
||||
TYPESYSTEM_SOURCE_ABSTRACT(Part::FaceMaker, Base::BaseClass)
|
||||
@@ -46,6 +48,11 @@ void Part::FaceMaker::addWire(const TopoDS_Wire& w)
|
||||
|
||||
void Part::FaceMaker::addShape(const TopoDS_Shape& sh)
|
||||
{
|
||||
addTopoShape(sh);
|
||||
}
|
||||
|
||||
void Part::FaceMaker::addTopoShape(const TopoShape& shape) {
|
||||
const TopoDS_Shape &sh = shape.getShape();
|
||||
if(sh.IsNull())
|
||||
throw Base::ValueError("Input shape is null.");
|
||||
switch(sh.ShapeType()){
|
||||
@@ -58,11 +65,14 @@ void Part::FaceMaker::addShape(const TopoDS_Shape& sh)
|
||||
case TopAbs_EDGE:
|
||||
this->myWires.push_back(BRepBuilderAPI_MakeWire(TopoDS::Edge(sh)).Wire());
|
||||
break;
|
||||
case TopAbs_FACE:
|
||||
this->myInputFaces.push_back(sh);
|
||||
break;
|
||||
default:
|
||||
throw Base::TypeError("Shape must be a wire, edge or compound. Something else was supplied.");
|
||||
break;
|
||||
}
|
||||
this->mySourceShapes.push_back(sh);
|
||||
this->mySourceShapes.push_back(shape);
|
||||
}
|
||||
|
||||
void Part::FaceMaker::useCompound(const TopoDS_Compound& comp)
|
||||
@@ -73,14 +83,29 @@ void Part::FaceMaker::useCompound(const TopoDS_Compound& comp)
|
||||
}
|
||||
}
|
||||
|
||||
void Part::FaceMaker::useTopoCompound(const TopoShape& comp)
|
||||
{
|
||||
for(auto &s : comp.getSubTopoShapes())
|
||||
this->addTopoShape(s);
|
||||
}
|
||||
|
||||
const TopoDS_Face& Part::FaceMaker::Face()
|
||||
{
|
||||
const TopoDS_Shape &sh = this->Shape();
|
||||
if(sh.IsNull())
|
||||
return TopoDS::Face(TopoFace().getShape());
|
||||
}
|
||||
|
||||
const Part::TopoShape &Part::FaceMaker::TopoFace() const{
|
||||
if(this->myTopoShape.isNull())
|
||||
throw NullShapeException("Part::FaceMaker: result shape is null.");
|
||||
if (sh.ShapeType() != TopAbs_FACE)
|
||||
if (this->myTopoShape.getShape().ShapeType() != TopAbs_FACE)
|
||||
throw Base::TypeError("Part::FaceMaker: return shape is not a single face.");
|
||||
return TopoDS::Face(sh);
|
||||
return this->myTopoShape;
|
||||
}
|
||||
|
||||
const Part::TopoShape &Part::FaceMaker::getTopoShape() const{
|
||||
if(this->myTopoShape.isNull())
|
||||
throw NullShapeException("Part::FaceMaker: result shape is null.");
|
||||
return this->myTopoShape;
|
||||
}
|
||||
|
||||
#if OCC_VERSION_HEX >= 0x070600
|
||||
@@ -90,7 +115,7 @@ void Part::FaceMaker::Build()
|
||||
#endif
|
||||
{
|
||||
this->NotDone();
|
||||
this->myShapesToReturn.clear();
|
||||
this->myShapesToReturn = this->myInputFaces;
|
||||
this->myGenerated.Clear();
|
||||
|
||||
this->Build_Essence();//adds stuff to myShapesToReturn
|
||||
@@ -118,6 +143,7 @@ void Part::FaceMaker::Build()
|
||||
|
||||
if(this->myShapesToReturn.empty()){
|
||||
//nothing to do, null shape will be returned.
|
||||
this->myShape = TopoDS_Shape();
|
||||
} else if (this->myShapesToReturn.size() == 1){
|
||||
this->myShape = this->myShapesToReturn[0];
|
||||
} else {
|
||||
@@ -129,6 +155,67 @@ void Part::FaceMaker::Build()
|
||||
}
|
||||
this->myShape = cmp_res;
|
||||
}
|
||||
|
||||
postBuild();
|
||||
}
|
||||
|
||||
struct ElementName {
|
||||
long tag;
|
||||
Data::MappedName name;
|
||||
Data::ElementIDRefs sids;
|
||||
|
||||
ElementName(long t, const Data::MappedName &n, const Data::ElementIDRefs &sids)
|
||||
:tag(t),name(n), sids(sids)
|
||||
{}
|
||||
|
||||
inline bool operator<(const ElementName &other) const {
|
||||
if(tag<other.tag)
|
||||
return true;
|
||||
if(tag>other.tag)
|
||||
return false;
|
||||
return Data::ElementNameComparator()(name,other.name);
|
||||
}
|
||||
};
|
||||
|
||||
void Part::FaceMaker::postBuild() {
|
||||
this->myTopoShape.setShape(this->myShape);
|
||||
this->myTopoShape.Hasher = this->MyHasher;
|
||||
this->myTopoShape.mapSubElement(this->mySourceShapes);
|
||||
int index = 0;
|
||||
const char *op = this->MyOp;
|
||||
if(!op)
|
||||
op = Part::OpCodes::Face;
|
||||
const auto &faces = this->myTopoShape.getSubTopoShapes(TopAbs_FACE);
|
||||
// name the face using the edges of its outer wire
|
||||
for(auto &face : faces) {
|
||||
++index;
|
||||
TopoShape wire = face.splitWires();
|
||||
wire.mapSubElement(face);
|
||||
std::set<ElementName> edgeNames;
|
||||
int count = wire.countSubShapes(TopAbs_EDGE);
|
||||
for (int index2 = 1; index2 <= count; ++index2) {
|
||||
Data::ElementIDRefs sids;
|
||||
Data::MappedName name =
|
||||
face.getMappedName(Data::IndexedName::fromConst("Edge", index2), false, &sids);
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
edgeNames.emplace(wire.getElementHistory(name), name, sids);
|
||||
}
|
||||
if (edgeNames.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<Data::MappedName> names;
|
||||
Data::ElementIDRefs sids;
|
||||
// We just use the first source element name to make the face name more
|
||||
// stable
|
||||
names.push_back(edgeNames.begin()->name);
|
||||
sids = edgeNames.begin()->sids;
|
||||
this->myTopoShape.setElementComboName(
|
||||
Data::IndexedName::fromConst("Face",index),names,op,nullptr,&sids);
|
||||
}
|
||||
this->myTopoShape.initCache(true);
|
||||
this->Done();
|
||||
}
|
||||
|
||||
@@ -172,12 +259,12 @@ TYPESYSTEM_SOURCE(Part::FaceMakerSimple, Part::FaceMakerPublic)
|
||||
|
||||
std::string Part::FaceMakerSimple::getUserFriendlyName() const
|
||||
{
|
||||
return {QT_TRANSLATE_NOOP("Part_FaceMaker","Simple")};
|
||||
return std::string(QT_TRANSLATE_NOOP("Part_FaceMaker","Simple"));
|
||||
}
|
||||
|
||||
std::string Part::FaceMakerSimple::getBriefExplanation() const
|
||||
{
|
||||
return {QT_TRANSLATE_NOOP("Part_FaceMaker","Makes separate plane face from every wire independently. No support for holes; wires can be on different planes.")};
|
||||
return std::string(QT_TRANSLATE_NOOP("Part_FaceMaker","Makes separate plane face from every wire independently. No support for holes; wires can be on different planes."));
|
||||
}
|
||||
|
||||
void Part::FaceMakerSimple::Build_Essence()
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include <Base/BaseClass.h>
|
||||
#include <Mod/Part/PartGlobal.h>
|
||||
|
||||
#include <App/StringHasher.h>
|
||||
#include "TopoShape.h"
|
||||
|
||||
namespace Part
|
||||
{
|
||||
@@ -47,11 +49,16 @@ namespace Part
|
||||
*/
|
||||
class PartExport FaceMaker: public BRepBuilderAPI_MakeShape, public Base::BaseClass
|
||||
{
|
||||
TYPESYSTEM_HEADER_WITH_OVERRIDE();
|
||||
TYPESYSTEM_HEADER();
|
||||
|
||||
public:
|
||||
FaceMaker() = default;
|
||||
~FaceMaker() override = default;
|
||||
FaceMaker() {}
|
||||
virtual ~FaceMaker() {}
|
||||
|
||||
void addTopoShape(const TopoShape &s);
|
||||
void useTopoCompound(const TopoShape &comp);
|
||||
const TopoShape &getTopoShape() const;
|
||||
const TopoShape &TopoFace() const;
|
||||
|
||||
virtual void addWire(const TopoDS_Wire& w);
|
||||
/**
|
||||
@@ -69,6 +76,8 @@ public:
|
||||
*/
|
||||
virtual void useCompound(const TopoDS_Compound &comp);
|
||||
|
||||
virtual void setPlane(const gp_Pln &) {}
|
||||
|
||||
/**
|
||||
* @brief Face: returns the face (result). If result is not a single face,
|
||||
* throws Base::TypeError. (hint: use .Shape() instead)
|
||||
@@ -79,7 +88,7 @@ public:
|
||||
#if OCC_VERSION_HEX >= 0x070600
|
||||
void Build(const Message_ProgressRange& theRange = Message_ProgressRange()) override;
|
||||
#else
|
||||
void Build() override;
|
||||
virtual void Build();
|
||||
#endif
|
||||
|
||||
//fails to compile, huh!
|
||||
@@ -90,11 +99,16 @@ public:
|
||||
static std::unique_ptr<FaceMaker> ConstructFromType(const char* className);
|
||||
static std::unique_ptr<FaceMaker> ConstructFromType(Base::Type type);
|
||||
|
||||
const char *MyOp = 0;
|
||||
App::StringHasherRef MyHasher;
|
||||
|
||||
protected:
|
||||
std::vector<TopoDS_Shape> mySourceShapes; //wire or compound
|
||||
std::vector<TopoShape> mySourceShapes; //wire or compound
|
||||
std::vector<TopoDS_Wire> myWires; //wires from mySourceShapes
|
||||
std::vector<TopoDS_Compound> myCompounds; //compounds, for recursive processing
|
||||
std::vector<TopoDS_Shape> myShapesToReturn;
|
||||
std::vector<TopoDS_Shape> myInputFaces;
|
||||
TopoShape myTopoShape;
|
||||
|
||||
/**
|
||||
* @brief Build_Essence: build routine that can assume there is no nesting.
|
||||
@@ -106,6 +120,7 @@ protected:
|
||||
* whole Build().
|
||||
*/
|
||||
virtual void Build_Essence() = 0;
|
||||
void postBuild();
|
||||
|
||||
static void throwNotImplemented();
|
||||
};
|
||||
@@ -115,7 +130,7 @@ protected:
|
||||
*/
|
||||
class PartExport FaceMakerPublic : public FaceMaker
|
||||
{
|
||||
TYPESYSTEM_HEADER_WITH_OVERRIDE();
|
||||
TYPESYSTEM_HEADER();
|
||||
public:
|
||||
virtual std::string getUserFriendlyName() const = 0;
|
||||
virtual std::string getBriefExplanation() const = 0;
|
||||
|
||||
@@ -356,14 +356,14 @@ void FaceMakerExtrusion::Build()
|
||||
if (mySourceShapes.empty())
|
||||
throw Base::ValueError("No input shapes!");
|
||||
if (mySourceShapes.size() == 1) {
|
||||
inputShape = mySourceShapes[0];
|
||||
inputShape = mySourceShapes[0].getShape();
|
||||
}
|
||||
else {
|
||||
TopoDS_Builder builder;
|
||||
TopoDS_Compound cmp;
|
||||
builder.MakeCompound(cmp);
|
||||
for (const TopoDS_Shape& sh : mySourceShapes) {
|
||||
builder.Add(cmp, sh);
|
||||
for (const auto &sh : mySourceShapes) {
|
||||
builder.Add(cmp, sh.getShape());
|
||||
}
|
||||
inputShape = cmp;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ class PartExport ShapeSegment: public Data::Segment
|
||||
TYPESYSTEM_HEADER_WITH_OVERRIDE();
|
||||
|
||||
public:
|
||||
ShapeSegment(const TopoDS_Shape& ShapeIn)
|
||||
explicit ShapeSegment(const TopoDS_Shape& ShapeIn)
|
||||
: Shape(ShapeIn)
|
||||
{}
|
||||
ShapeSegment() = default;
|
||||
@@ -257,7 +257,7 @@ public:
|
||||
bool analyze(bool runBopCheck, std::ostream&) const;
|
||||
bool isClosed() const;
|
||||
bool isCoplanar(const TopoShape& other, double tol = -1) const;
|
||||
bool findPlane(gp_Pln& pln, double tol = -1) const;
|
||||
bool findPlane(gp_Pln& plane, double tol = -1) const;
|
||||
/// Returns true if the expansion of the shape is infinite, false otherwise
|
||||
bool isInfinite() const;
|
||||
/// Checks whether the shape is a planar face
|
||||
@@ -387,6 +387,27 @@ public:
|
||||
TopoDS_Shape defeaturing(const std::vector<TopoDS_Shape>& s) const;
|
||||
TopoDS_Shape makeShell(const TopoDS_Shape&) const;
|
||||
//@}
|
||||
|
||||
/// Wire re-orientation when calling splitWires()
|
||||
enum SplitWireReorient {
|
||||
/// Keep original reorientation
|
||||
NoReorient,
|
||||
/// Make outer wire forward, and inner wires reversed
|
||||
Reorient,
|
||||
/// Make both outer and inner wires forward
|
||||
ReorientForward,
|
||||
/// Make both outer and inner wires reversed
|
||||
ReorientReversed,
|
||||
};
|
||||
/** Return the outer and inner wires of a face
|
||||
*
|
||||
* @param inner: optional output of inner wires
|
||||
* @param reorient: wire reorientation, see SplitWireReorient
|
||||
*
|
||||
* @return Return the outer wire
|
||||
*/
|
||||
TopoShape splitWires(std::vector<TopoShape> *inner = nullptr,
|
||||
SplitWireReorient reorient = Reorient) const;
|
||||
|
||||
/** @name Element name mapping aware shape maker
|
||||
*
|
||||
@@ -571,6 +592,11 @@ public:
|
||||
const std::string& shapeName(bool silent = false) const;
|
||||
static std::pair<TopAbs_ShapeEnum, int> shapeTypeAndIndex(const char* name);
|
||||
|
||||
Data::MappedName setElementComboName(const Data::IndexedName & element,
|
||||
const std::vector<Data::MappedName> &names,
|
||||
const char *marker=nullptr,
|
||||
const char *op=nullptr,
|
||||
const Data::ElementIDRefs *sids=nullptr);
|
||||
|
||||
/** @name sub shape cached functions
|
||||
*
|
||||
@@ -650,6 +676,59 @@ public:
|
||||
*/
|
||||
TopoShape &makeElementCompound(const std::vector<TopoShape> &shapes, const char *op=nullptr, bool force=true);
|
||||
|
||||
TopoShape& makeElementFace(const std::vector<TopoShape>& shapes,
|
||||
const char* op = nullptr,
|
||||
const char* maker = nullptr,
|
||||
const gp_Pln* plane = nullptr);
|
||||
/** Make a planar face with the input wire or edge
|
||||
*
|
||||
* @param shape: input shape. Can be either edge, wire, or compound of
|
||||
* those two types
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
* @param maker: optional type name of the face maker. If not given,
|
||||
* default to "Part::FaceMakerBullseye"
|
||||
* @param plane: optional plane of the face.
|
||||
*
|
||||
* @return The function creates a planar face. The original content of this
|
||||
* TopoShape is discarded and replaced with the new shape. The
|
||||
* function returns the TopoShape itself as a reference so that
|
||||
* multiple operations can be carried out for the same shape in the
|
||||
* same line of code.
|
||||
*/
|
||||
TopoShape& makeElementFace(const TopoShape& shape,
|
||||
const char* op = nullptr,
|
||||
const char* maker = nullptr,
|
||||
const gp_Pln* plane = nullptr);
|
||||
/** Make a planar face using this shape
|
||||
*
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
* @param maker: optional type name of the face maker. If not given,
|
||||
* default to "Part::FaceMakerBullseye"
|
||||
* @param plane: optional plane of the face.
|
||||
*
|
||||
* @return The function returns a new planar face made using the wire or edge
|
||||
* inside this shape. The shape itself is not modified.
|
||||
*/
|
||||
TopoShape makeElementFace(const char* op = nullptr,
|
||||
const char* maker = nullptr,
|
||||
const gp_Pln* plane = nullptr) const
|
||||
{
|
||||
return TopoShape(0, Hasher).makeElementFace(*this, op, maker, plane);
|
||||
}
|
||||
|
||||
/// Filling style when making a BSpline face
|
||||
enum class FillingStyle
|
||||
{
|
||||
/// The style with the flattest patches
|
||||
Stretch,
|
||||
/// A rounded style of patch with less depth than those of Curved
|
||||
Coons,
|
||||
/// The style with the most rounded patches
|
||||
Curved,
|
||||
};
|
||||
|
||||
struct BRepFillingParams;
|
||||
|
||||
/** Provides information about the continuity of a curve.
|
||||
|
||||
@@ -26,11 +26,19 @@
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRepTools.hxx>
|
||||
|
||||
#include <ShapeFix_Shape.hxx>
|
||||
#include <ShapeFix_ShapeTolerance.hxx>
|
||||
|
||||
#endif
|
||||
|
||||
#include "TopoShape.h"
|
||||
#include "TopoShapeCache.h"
|
||||
#include "FaceMaker.h"
|
||||
|
||||
|
||||
FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT
|
||||
|
||||
@@ -475,7 +483,7 @@ void TopoShape::mapCompoundSubElements(const std::vector<TopoShape>& shapes, con
|
||||
++count;
|
||||
auto subshape = getSubShape(TopAbs_SHAPE, count, /*silent = */ true);
|
||||
if (!subshape.IsPartner(topoShape._Shape)) {
|
||||
return; // Not a partner shape, don't do any mapping at all
|
||||
return; // Not a partner shape, don't do any mapping at all
|
||||
}
|
||||
}
|
||||
auto children {createChildMap(count, shapes, op)};
|
||||
@@ -543,4 +551,250 @@ TopoShape::makeElementCompound(const std::vector<TopoShape>& shapes, const char*
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
TopoShape& TopoShape::makeElementFace(const TopoShape& shape,
|
||||
const char* op,
|
||||
const char* maker,
|
||||
const gp_Pln* plane)
|
||||
{
|
||||
std::vector<TopoShape> shapes;
|
||||
if (shape.isNull()) {
|
||||
FC_THROWM(NullShapeException, "Null shape");
|
||||
}
|
||||
if (shape.getShape().ShapeType() == TopAbs_COMPOUND) {
|
||||
shapes = shape.getSubTopoShapes();
|
||||
}
|
||||
else {
|
||||
shapes.push_back(shape);
|
||||
}
|
||||
return makeElementFace(shapes, op, maker, plane);
|
||||
}
|
||||
|
||||
TopoShape& TopoShape::makeElementFace(const std::vector<TopoShape>& shapes,
|
||||
const char* op,
|
||||
const char* maker,
|
||||
const gp_Pln* plane)
|
||||
{
|
||||
if (!maker || !maker[0]) {
|
||||
maker = "Part::FaceMakerBullseye";
|
||||
}
|
||||
std::unique_ptr<FaceMaker> mkFace = FaceMaker::ConstructFromType(maker);
|
||||
mkFace->MyHasher = Hasher;
|
||||
mkFace->MyOp = op;
|
||||
if (plane) {
|
||||
mkFace->setPlane(*plane);
|
||||
}
|
||||
|
||||
for (auto& shape : shapes) {
|
||||
if (shape.getShape().ShapeType() == TopAbs_COMPOUND) {
|
||||
mkFace->useTopoCompound(shape);
|
||||
}
|
||||
else {
|
||||
mkFace->addTopoShape(shape);
|
||||
}
|
||||
}
|
||||
mkFace->Build();
|
||||
|
||||
const auto& ret = mkFace->getTopoShape();
|
||||
setShape(ret._Shape);
|
||||
Hasher = ret.Hasher;
|
||||
resetElementMap(ret.elementMap());
|
||||
if (!isValid()) {
|
||||
ShapeFix_ShapeTolerance aSFT;
|
||||
aSFT.LimitTolerance(getShape(),
|
||||
Precision::Confusion(),
|
||||
Precision::Confusion(),
|
||||
TopAbs_SHAPE);
|
||||
|
||||
// In some cases, the OCC reports the returned shape having invalid
|
||||
// tolerance. Not sure about the real cause.
|
||||
//
|
||||
// Update: one of the cause is related to OCC bug in
|
||||
// BRepBuilder_FindPlane, A possible call sequence is,
|
||||
//
|
||||
// makEOffset2D() -> TopoShape::findPlane() -> BRepLib_FindSurface
|
||||
//
|
||||
// See code comments in findPlane() for the description of the bug and
|
||||
// work around.
|
||||
|
||||
ShapeFix_Shape fixer(getShape());
|
||||
fixer.Perform();
|
||||
setShape(fixer.Shape(), false);
|
||||
|
||||
if (!isValid()) {
|
||||
FC_WARN("makeElementFace: resulting face is invalid");
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode and set an element name in the elementMap. If a hasher is defined, apply it to the name.
|
||||
*
|
||||
* @param element The element name(type) that provides 1 one character suffix to the name IF <conditions>.
|
||||
* @param names The subnames to build the name from. If empty, return the TopoShape MappedName.
|
||||
* @param marker The elementMap name or suffix to start the name with. If null, use the
|
||||
* elementMapPrefix.
|
||||
* @param op The op text passed to the element name encoder along with the TopoShape Tag
|
||||
* @param _sids If defined, records the sub ids processed.
|
||||
*
|
||||
* @return The encoded, possibly hashed name.
|
||||
*/
|
||||
Data::MappedName TopoShape::setElementComboName(const Data::IndexedName& element,
|
||||
const std::vector<Data::MappedName>& names,
|
||||
const char* marker,
|
||||
const char* op,
|
||||
const Data::ElementIDRefs* _sids)
|
||||
{
|
||||
if (names.empty()) {
|
||||
return Data::MappedName();
|
||||
}
|
||||
std::string _marker;
|
||||
if (!marker) {
|
||||
marker = elementMapPrefix().c_str();
|
||||
}
|
||||
else if (!boost::starts_with(marker, elementMapPrefix())) {
|
||||
_marker = elementMapPrefix() + marker;
|
||||
marker = _marker.c_str();
|
||||
}
|
||||
auto it = names.begin();
|
||||
Data::MappedName newName = *it;
|
||||
std::ostringstream ss;
|
||||
Data::ElementIDRefs sids;
|
||||
if (_sids) {
|
||||
sids = *_sids;
|
||||
}
|
||||
if (names.size() == 1) {
|
||||
ss << marker;
|
||||
}
|
||||
else {
|
||||
bool first = true;
|
||||
ss.str("");
|
||||
if (!Hasher) {
|
||||
ss << marker;
|
||||
}
|
||||
ss << '(';
|
||||
for (++it; it != names.end(); ++it) {
|
||||
if (first) {
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
ss << '|';
|
||||
}
|
||||
ss << *it;
|
||||
}
|
||||
ss << ')';
|
||||
if (Hasher) {
|
||||
sids.push_back(Hasher->getID(ss.str().c_str()));
|
||||
ss.str("");
|
||||
ss << marker << sids.back().toString();
|
||||
}
|
||||
}
|
||||
elementMap()->encodeElementName(element[0], newName, ss, &sids, Tag, op);
|
||||
return elementMap()->setElementName(element, newName, Tag, &sids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorient the outer and inner wires of the TopoShape
|
||||
*
|
||||
* @param inner If this is not a nullptr, then any inner wires processed will be returned in this
|
||||
* vector.
|
||||
* @param reorient One of NoReorient, Reorient ( Outer forward, inner reversed ),
|
||||
* ReorientForward ( all forward ), or ReorientReversed ( all reversed )
|
||||
* @return The outer wire, or an empty TopoShape if this isn't a Face, has no Face subShapes, or the
|
||||
* outer wire isn't found.
|
||||
*/
|
||||
TopoShape TopoShape::splitWires(std::vector<TopoShape>* inner, SplitWireReorient reorient) const
|
||||
{
|
||||
// ShapeAnalysis::OuterWire() is un-reliable for some reason. OCC source
|
||||
// code shows it works by creating face using each wire, and then test using
|
||||
// BRepTopAdaptor_FClass2d::PerformInfinitePoint() to check if it is an out
|
||||
// bound wire. And practice shows it sometimes returns the incorrect
|
||||
// result. Need more investigation. Note that this may be related to
|
||||
// unreliable solid face orientation
|
||||
// (https://forum.freecadweb.org/viewtopic.php?p=446006#p445674)
|
||||
//
|
||||
// Use BrepTools::OuterWire() instead. OCC source code shows it is
|
||||
// implemented using simple bound box checking. This should be a
|
||||
// reliable method, especially so for a planar face.
|
||||
|
||||
TopoDS_Shape tmp;
|
||||
if (shapeType(true) == TopAbs_FACE) {
|
||||
tmp = BRepTools::OuterWire(TopoDS::Face(_Shape));
|
||||
}
|
||||
else if (countSubShapes(TopAbs_FACE) == 1) {
|
||||
tmp = BRepTools::OuterWire(TopoDS::Face(getSubShape(TopAbs_FACE, 1)));
|
||||
}
|
||||
if (tmp.IsNull()) {
|
||||
return TopoShape();
|
||||
}
|
||||
const auto& wires = getSubTopoShapes(TopAbs_WIRE);
|
||||
auto it = wires.begin();
|
||||
|
||||
TopAbs_Orientation orientOuter, orientInner;
|
||||
switch (reorient) {
|
||||
case ReorientReversed:
|
||||
orientOuter = orientInner = TopAbs_REVERSED;
|
||||
break;
|
||||
case ReorientForward:
|
||||
orientOuter = orientInner = TopAbs_FORWARD;
|
||||
break;
|
||||
default:
|
||||
orientOuter = TopAbs_FORWARD;
|
||||
orientInner = TopAbs_REVERSED;
|
||||
break;
|
||||
}
|
||||
|
||||
auto doReorient = [](TopoShape& s, TopAbs_Orientation orient) {
|
||||
// Special case of single edge wire. Make sure the edge is in the
|
||||
// required orientation. This is necessary because BRepFill_OffsetWire
|
||||
// has special handling of circular edge offset, which seem to only
|
||||
// respect the edge orientation and disregard the wire orientation. The
|
||||
// orientation is used to determine whether to shrink or expand.
|
||||
if (s.countSubShapes(TopAbs_EDGE) == 1) {
|
||||
TopoDS_Shape e = s.getSubShape(TopAbs_EDGE, 1);
|
||||
if (e.Orientation() == orient) {
|
||||
if (s._Shape.Orientation() == orient) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
e = e.Oriented(orient);
|
||||
}
|
||||
BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(e));
|
||||
s.setShape(mkWire.Shape(), false);
|
||||
}
|
||||
else if (s._Shape.Orientation() != orient) {
|
||||
s.setShape(s._Shape.Oriented(orient), false);
|
||||
}
|
||||
};
|
||||
|
||||
for (; it != wires.end(); ++it) {
|
||||
auto& wire = *it;
|
||||
if (wire.getShape().IsSame(tmp)) {
|
||||
if (inner) {
|
||||
for (++it; it != wires.end(); ++it) {
|
||||
inner->push_back(*it);
|
||||
if (reorient) {
|
||||
doReorient(inner->back(), orientInner);
|
||||
}
|
||||
}
|
||||
}
|
||||
auto res = wire;
|
||||
if (reorient) {
|
||||
doReorient(res, orientOuter);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
if (inner) {
|
||||
inner->push_back(wire);
|
||||
if (reorient) {
|
||||
doReorient(inner->back(), orientInner);
|
||||
}
|
||||
}
|
||||
}
|
||||
return TopoShape();
|
||||
}
|
||||
|
||||
|
||||
} // namespace Part
|
||||
|
||||
@@ -14,6 +14,21 @@ double getVolume(const TopoDS_Shape& shape)
|
||||
return prop.Mass();
|
||||
}
|
||||
|
||||
double getArea(const TopoDS_Shape& shape)
|
||||
{
|
||||
GProp_GProps prop;
|
||||
BRepGProp::SurfaceProperties(shape, prop);
|
||||
return prop.Mass();
|
||||
}
|
||||
|
||||
double getLength(const TopoDS_Shape& shape)
|
||||
{
|
||||
GProp_GProps prop;
|
||||
BRepGProp::LinearProperties(shape, prop);
|
||||
return prop.Mass();
|
||||
}
|
||||
|
||||
|
||||
void PartTestHelperClass::createTestDoc()
|
||||
{
|
||||
_docName = App::GetApplication().getUniqueDocumentName("test");
|
||||
@@ -48,7 +63,8 @@ _getFilletEdges(const std::vector<int>& edges, double startRadius, double endRad
|
||||
return filletElements;
|
||||
}
|
||||
|
||||
void executePython(const std::vector<std::string>& python)
|
||||
|
||||
void ExecutePython(const std::vector<std::string>& python)
|
||||
{
|
||||
Base::InterpreterSingleton is = Base::InterpreterSingleton();
|
||||
|
||||
@@ -68,16 +84,9 @@ void rectangle(double height, double width, char* name)
|
||||
boost::str(boost::format("V4 = FreeCAD.Vector(0, %d, 0)") % width),
|
||||
"P1 = Part.makePolygon([V1, V2, V3, V4],True)",
|
||||
"F1 = Part.Face(P1)", // Make the face or the volume calc won't work right.
|
||||
// "L1 = Part.LineSegment(V1, V2)",
|
||||
// "L2 = Part.LineSegment(V2, V3)",
|
||||
// "L3 = Part.LineSegment(V3, V4)",
|
||||
// "L4 = Part.LineSegment(V4, V1)",
|
||||
// "S1 = Part.Shape([L1,L2,L3,L4])",
|
||||
// "W1 = Part.Wire(S1.Edges)",
|
||||
// "F1 = Part.Face(W1)", // Make the face or the volume calc won't work right.
|
||||
boost::str(boost::format("Part.show(F1,'%s')") % name),
|
||||
};
|
||||
executePython(rectstring);
|
||||
ExecutePython(rectstring);
|
||||
}
|
||||
|
||||
testing::AssertionResult
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <boost/format.hpp>
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include "Base/Interpreter.h"
|
||||
#include <Base/Precision.h>
|
||||
#include "Mod/Part/App/FeaturePartBox.h"
|
||||
#include "Mod/Part/App/FeaturePartFuse.h"
|
||||
#include "Mod/Part/App/FeatureFillet.h"
|
||||
#include <BRepGProp.hxx>
|
||||
#include "Base/Interpreter.h"
|
||||
#include <boost/format.hpp>
|
||||
|
||||
namespace PartTestHelpers
|
||||
{
|
||||
|
||||
double getVolume(const TopoDS_Shape& shape);
|
||||
|
||||
double getArea(const TopoDS_Shape& shape);
|
||||
|
||||
double getLength(const TopoDS_Shape& shape);
|
||||
|
||||
std::vector<Part::FilletElement>
|
||||
_getFilletEdges(const std::vector<int>& edges, double startRadius, double endRadius);
|
||||
|
||||
|
||||
@@ -3,12 +3,18 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "src/App/InitApplication.h"
|
||||
#include <Mod/Part/App/TopoShape.h>
|
||||
#include <Mod/Part/App/TopoShapeOpCode.h>
|
||||
|
||||
#include "PartTestHelpers.h"
|
||||
|
||||
#include <BRepAlgoAPI_Fuse.hxx>
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <BRepPrimAPI_MakeBox.hxx>
|
||||
#include <GC_MakeCircle.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Edge.hxx>
|
||||
#include <BRep_Builder.hxx>
|
||||
|
||||
// NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)
|
||||
|
||||
@@ -35,6 +41,7 @@ protected:
|
||||
App::GetApplication().closeDocument(_docName.c_str());
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::string _docName;
|
||||
Data::ElementIDRefs _sid;
|
||||
@@ -162,6 +169,273 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes)
|
||||
// 26 subshapes each
|
||||
}
|
||||
|
||||
std::tuple<TopoDS_Face, TopoDS_Wire, TopoDS_Edge, TopoDS_Edge, TopoDS_Edge, TopoDS_Edge>
|
||||
CreateRectFace(float len = 2.0, float wid = 3.0)
|
||||
{
|
||||
auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(len, 0.0, 0.0)).Edge();
|
||||
auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(len, 0.0, 0.0), gp_Pnt(len, wid, 0.0)).Edge();
|
||||
auto edge3 = BRepBuilderAPI_MakeEdge(gp_Pnt(len, wid, 0.0), gp_Pnt(0.0, wid, 0.0)).Edge();
|
||||
auto edge4 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, wid, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge();
|
||||
auto wire1 = BRepBuilderAPI_MakeWire({edge1, edge2, edge3, edge4}).Wire();
|
||||
auto face1 = BRepBuilderAPI_MakeFace(wire1).Face();
|
||||
return {face1, wire1, edge1, edge2, edge3, edge4};
|
||||
}
|
||||
|
||||
std::tuple<TopoDS_Face, TopoDS_Wire, TopoDS_Wire>
|
||||
CreateFaceWithRoundHole(float len = 2.0, float wid = 3.0, float radius = 1.0)
|
||||
{
|
||||
auto [face1, wire1, edge1, edge2, edge3, edge4] = CreateRectFace(len, wid);
|
||||
auto circ1 =
|
||||
GC_MakeCircle(gp_Pnt(len / 2.0, wid / 2.0, 0), gp_Dir(0.0, 0.0, 1.0), radius).Value();
|
||||
auto edge5 = BRepBuilderAPI_MakeEdge(circ1).Edge();
|
||||
auto wire2 = BRepBuilderAPI_MakeWire(edge5).Wire();
|
||||
auto face2 = BRepBuilderAPI_MakeFace(face1, wire2).Face();
|
||||
return {face2, wire1, wire2};
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, makeElementFaceNull)
|
||||
{
|
||||
// Arrange
|
||||
const double Len = 3, Wid = 2, Rad = 1;
|
||||
auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad);
|
||||
Part::TopoShape topoShape {face1};
|
||||
double area = PartTestHelpers::getArea(face1);
|
||||
double area1 = PartTestHelpers::getArea(topoShape.getShape());
|
||||
// Act
|
||||
Part::TopoShape newFace = topoShape.makeElementFace(nullptr);
|
||||
double area2 = PartTestHelpers::getArea(newFace.getShape());
|
||||
double area3 = PartTestHelpers::getArea(topoShape.getShape());
|
||||
// Assert
|
||||
EXPECT_FALSE(face1.IsEqual(newFace.getShape()));
|
||||
EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area1, Len * Wid + M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area2, Len * Wid - M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area3, Len * Wid + M_PI * Rad * Rad);
|
||||
EXPECT_STREQ(newFace.shapeName().c_str(), "Face");
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, makeElementFaceSimple)
|
||||
{
|
||||
// Arrange
|
||||
const double Len = 3, Wid = 2, Rad = 1;
|
||||
auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad);
|
||||
Part::TopoShape topoShape {face1};
|
||||
double area = PartTestHelpers::getArea(face1);
|
||||
double area1 = PartTestHelpers::getArea(topoShape.getShape());
|
||||
// Act
|
||||
Part::TopoShape newFace = topoShape.makeElementFace(wire1);
|
||||
double area2 = PartTestHelpers::getArea(newFace.getShape());
|
||||
double area3 = PartTestHelpers::getArea(topoShape.getShape());
|
||||
// Assert
|
||||
EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered
|
||||
EXPECT_FALSE(face1.IsEqual(newFace.getShape()));
|
||||
EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area1, Len * Wid + M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area2, Len * Wid);
|
||||
EXPECT_FLOAT_EQ(area3, Len * Wid);
|
||||
EXPECT_STREQ(newFace.shapeName().c_str(), "Face");
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, makeElementFaceParams)
|
||||
{
|
||||
// Arrange
|
||||
const double Len = 3, Wid = 2, Rad = 1;
|
||||
auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad);
|
||||
Part::TopoShape topoShape {face1, 1L};
|
||||
double area = PartTestHelpers::getArea(face1);
|
||||
double area1 = PartTestHelpers::getArea(topoShape.getShape());
|
||||
// Act
|
||||
Part::TopoShape newFace =
|
||||
topoShape.makeElementFace(wire1, "Cut", "Part::FaceMakerBullseye", nullptr);
|
||||
double area2 = PartTestHelpers::getArea(newFace.getShape());
|
||||
double area3 = PartTestHelpers::getArea(topoShape.getShape());
|
||||
// Assert
|
||||
EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered
|
||||
EXPECT_FALSE(face1.IsEqual(newFace.getShape()));
|
||||
EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area1, Len * Wid + M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area2, Len * Wid);
|
||||
EXPECT_FLOAT_EQ(area3, Len * Wid);
|
||||
EXPECT_STREQ(newFace.shapeName().c_str(), "Face");
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, makeElementFaceFromFace)
|
||||
{
|
||||
// Arrange
|
||||
const double Len = 3, Wid = 2, Rad = 1;
|
||||
auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad);
|
||||
Part::TopoShape topoShape {face1, 1L};
|
||||
double area = PartTestHelpers::getArea(face1);
|
||||
double area1 = PartTestHelpers::getArea(topoShape.getShape());
|
||||
// Act
|
||||
Part::TopoShape newFace =
|
||||
topoShape.makeElementFace(face1, "Cut", "Part::FaceMakerBullseye", nullptr);
|
||||
double area2 = PartTestHelpers::getArea(newFace.getShape());
|
||||
double area3 = PartTestHelpers::getArea(topoShape.getShape());
|
||||
// Assert
|
||||
EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered
|
||||
EXPECT_FALSE(face1.IsEqual(newFace.getShape()));
|
||||
EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area1, Len * Wid + M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area2, Len * Wid - M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area3, Len * Wid - M_PI * Rad * Rad);
|
||||
EXPECT_STREQ(newFace.shapeName().c_str(), "Face");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, makeElementFaceOpenWire)
|
||||
{
|
||||
// Arrange
|
||||
const double Len = 3, Wid = 2, Rad = 1;
|
||||
auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad);
|
||||
Part::TopoShape topoShape {wire1, 1L};
|
||||
double area = PartTestHelpers::getArea(face1);
|
||||
double area1 = PartTestHelpers::getArea(topoShape.getShape());
|
||||
// Act
|
||||
Part::TopoShape newFace = topoShape.makeElementFace(wire1, "Cut", nullptr, nullptr);
|
||||
double area2 = PartTestHelpers::getArea(newFace.getShape());
|
||||
double area3 = PartTestHelpers::getArea(topoShape.getShape());
|
||||
// Assert
|
||||
EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered
|
||||
EXPECT_FALSE(face1.IsEqual(newFace.getShape()));
|
||||
EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area1, 0); // Len * Wid - M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area2, Len * Wid);
|
||||
EXPECT_FLOAT_EQ(area3, Len * Wid);
|
||||
EXPECT_STREQ(newFace.shapeName().c_str(), "Face");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, makeElementFaceClosedWire)
|
||||
{
|
||||
// Arrange
|
||||
const double Len = 3, Wid = 2, Rad = 1;
|
||||
auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad);
|
||||
Part::TopoShape topoShape {wire2, 1L};
|
||||
double area = PartTestHelpers::getArea(face1);
|
||||
double area1 = PartTestHelpers::getArea(topoShape.getShape());
|
||||
// Act
|
||||
Part::TopoShape newFace =
|
||||
topoShape.makeElementFace(wire2, "Cut", "Part::FaceMakerBullseye", nullptr);
|
||||
double area2 = PartTestHelpers::getArea(newFace.getShape());
|
||||
double area3 = PartTestHelpers::getArea(topoShape.getShape());
|
||||
// Assert
|
||||
EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered
|
||||
EXPECT_FALSE(face1.IsEqual(newFace.getShape()));
|
||||
EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area1, 0); // Len * Wid - M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area2, M_PI * Rad * Rad);
|
||||
EXPECT_FLOAT_EQ(area3, M_PI * Rad * Rad);
|
||||
EXPECT_STREQ(newFace.shapeName().c_str(), "Face");
|
||||
}
|
||||
|
||||
// Possible future makeElementFace tests:
|
||||
// Overlapping wire
|
||||
// Compound of wires
|
||||
// Compound of faces
|
||||
// Compound of other shape types
|
||||
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, setElementComboNameNothing)
|
||||
{
|
||||
// Arrange
|
||||
Part::TopoShape topoShape(1L);
|
||||
// Act
|
||||
Data::MappedName result = topoShape.setElementComboName(Data::IndexedName(), {});
|
||||
// ASSERT
|
||||
EXPECT_STREQ(result.toString().c_str(), "");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, setElementComboNameSimple)
|
||||
{
|
||||
// Arrange
|
||||
auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge();
|
||||
Part::TopoShape topoShape(edge1, 1L);
|
||||
topoShape.setElementMap({}); // Initialize the map to avoid a segfault.
|
||||
// Also, maybe the end of TopoShape::mapSubElementTypeForShape should enforce that elementMap()
|
||||
// isn't nullptr to eliminate the segfault.
|
||||
Data::MappedName edgeName("testname");
|
||||
// Act
|
||||
Data::MappedName result =
|
||||
topoShape.setElementComboName(Data::IndexedName::fromConst("Edge", 1), {edgeName});
|
||||
// Assert
|
||||
EXPECT_STREQ(result.toString().c_str(), "testname;");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, setElementComboName)
|
||||
{
|
||||
// Arrange
|
||||
Part::TopoShape topoShape(2L);
|
||||
topoShape.setElementMap({});
|
||||
Data::MappedName edgeName =
|
||||
topoShape.getMappedName(Data::IndexedName::fromConst("Edge", 1), true);
|
||||
Data::MappedName faceName =
|
||||
topoShape.getMappedName(Data::IndexedName::fromConst("Face", 7), true);
|
||||
Data::MappedName faceName2 =
|
||||
topoShape.getMappedName(Data::IndexedName::fromConst("Face", 8), true);
|
||||
char* op = "Copy";
|
||||
// Act
|
||||
Data::MappedName result = topoShape.setElementComboName(Data::IndexedName::fromConst("Edge", 1),
|
||||
{edgeName, faceName, faceName2},
|
||||
Part::OpCodes::Common,
|
||||
op);
|
||||
// Assert
|
||||
EXPECT_STREQ(result.toString().c_str(), "Edge1;CMN(Face7|Face8);Copy");
|
||||
// The detailed forms of names are covered in encodeElementName tests
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, setElementComboNameCompound)
|
||||
{
|
||||
// Arrange
|
||||
auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge();
|
||||
auto wire1 = BRepBuilderAPI_MakeWire({edge1}).Wire();
|
||||
auto wire2 = BRepBuilderAPI_MakeWire({edge1}).Wire();
|
||||
Part::TopoShape topoShape(2L);
|
||||
topoShape.makeElementCompound({wire1, wire2}); // Quality of shape doesn't matter
|
||||
Data::MappedName edgeName =
|
||||
topoShape.getMappedName(Data::IndexedName::fromConst("Edge", 1), true);
|
||||
Data::MappedName faceName =
|
||||
topoShape.getMappedName(Data::IndexedName::fromConst("Face", 7), true);
|
||||
Data::MappedName faceName2 =
|
||||
topoShape.getMappedName(Data::IndexedName::fromConst("Face", 8), true);
|
||||
char* op = "Copy";
|
||||
// Act
|
||||
Data::MappedName result = topoShape.setElementComboName(Data::IndexedName::fromConst("Edge", 1),
|
||||
{edgeName, faceName, faceName2},
|
||||
Part::OpCodes::Common,
|
||||
op);
|
||||
// ASSERT
|
||||
EXPECT_STREQ(result.toString().c_str(), "Edge1;:H,E;CMN(Face7|Face8);Copy");
|
||||
// The detailed forms of names are covered in encodeElementName tests
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, splitWires)
|
||||
{
|
||||
// Arrange
|
||||
const double Len = 3, Wid = 2, Rad = 1;
|
||||
auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad);
|
||||
Part::TopoShape topoShape {face1, 1L};
|
||||
std::vector<Part::TopoShape> inner;
|
||||
// Act
|
||||
EXPECT_EQ(topoShape.getShape().Orientation(), TopAbs_FORWARD);
|
||||
Part::TopoShape wire =
|
||||
topoShape.splitWires(&inner, Part::TopoShape::SplitWireReorient::ReorientReversed);
|
||||
// Assert
|
||||
EXPECT_EQ(inner.size(), 1);
|
||||
EXPECT_FLOAT_EQ(PartTestHelpers::getLength(wire.getShape()), 2 + 2 + 3 + 3);
|
||||
EXPECT_FLOAT_EQ(PartTestHelpers::getLength(inner.front().getShape()), M_PI * Rad * 2);
|
||||
EXPECT_EQ(wire.getShape().Orientation(), TopAbs_REVERSED);
|
||||
for (Part::TopoShape& shape : inner) {
|
||||
EXPECT_EQ(shape.getShape().Orientation(), TopAbs_FORWARD);
|
||||
}
|
||||
}
|
||||
|
||||
// Possible future tests:
|
||||
// splitWires without inner Wires
|
||||
// splitWires with allfour reorientation values NoReorient, ReOrient, ReorientForward,
|
||||
// ReorientRevesed
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, mapSubElementInvalidParm)
|
||||
{
|
||||
@@ -310,5 +584,4 @@ TEST_F(TopoShapeExpansionTest, mapSubElementFindAncestors)
|
||||
EXPECT_TRUE(ancestorShapeList.back().IsEqual(topoShape6.getShape()));
|
||||
}
|
||||
|
||||
|
||||
// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)
|
||||
|
||||
Reference in New Issue
Block a user