Toponaming/Part: Clean and add tests

This commit is contained in:
bgbsww
2024-02-15 21:22:50 -05:00
parent d80758e882
commit 921be4daaa
6 changed files with 130 additions and 51 deletions

View File

@@ -140,6 +140,24 @@ enum class LinearizeEdge
linearizeEdges
};
enum class IsSolid
{
notSolid,
solid
};
enum class IsRuled
{
notRuled,
ruled
};
enum class IsClosed
{
notClosed,
closed
};
/** The representation for a CAD Shape
*/
// NOLINTNEXTLINE cppcoreguidelines-special-member-functions
@@ -793,10 +811,10 @@ public:
* type, but only wires are used. The first and last
* section may be vertex.
* @param isSolid: whether to make a solid
* @param isRuled: If true, then the faces generated between the edges of
* @param isRuled: if isRuled then the faces generated between the edges of
* two consecutive section wires are ruled surfaces. If
* false, then they are smoothed out by approximation
* @param isClosed: If true, then the first section is duplicated to close
* notRuled, then they are smoothed out by approximation
* @param isClosed: If isClosed, then the first section is duplicated to close
* the loft as the last section
* @param maxDegree: define the maximal U degree of the result surface
* @param op: optional string to be encoded into topo naming for indicating
@@ -807,8 +825,8 @@ public:
* a self reference so that multiple operations can be carried out
* for the same shape in the same line of code.
*/
TopoShape &makELoft(const std::vector<TopoShape> &sources,
Standard_Boolean isSolid, Standard_Boolean isRuled, Standard_Boolean isClosed=Standard_False,
TopoShape &makeElementLoft(const std::vector<TopoShape> &sources,
IsSolid isSolid, IsRuled isRuled, IsClosed isClosed=IsClosed::notClosed,
Standard_Integer maxDegree=5, const char *op=nullptr);
/** Make a ruled surface

View File

@@ -1737,8 +1737,8 @@ TopoShape& TopoShape::makeElementRuledSurface(const std::vector<TopoShape>& shap
if (orientation == 0) {
// Automatic
Handle(Adaptor3d_Curve) a1;
Handle(Adaptor3d_Curve) a2;
Handle(Adaptor3d_HCurve) a1;
Handle(Adaptor3d_HCurve) a2;
if (!isWire) {
BRepAdaptor_HCurve adapt1(TopoDS::Edge(S1.getShape()));
BRepAdaptor_HCurve adapt2(TopoDS::Edge(S2.getShape()));
@@ -1840,35 +1840,43 @@ TopoShape& TopoShape::makeElementCompound(const std::vector<TopoShape>& shapes,
return *this;
}
static std::vector<TopoShape> prepareProfiles(const std::vector<TopoShape> &shapes,size_t offset=0) {
static std::vector<TopoShape> prepareProfiles(const std::vector<TopoShape>& shapes,
size_t offset = 0)
{
std::vector<TopoShape> ret;
for(size_t i=offset;i<shapes.size();++i) {
for (size_t i = offset; i < shapes.size(); ++i) {
auto sh = shapes[i];
if(sh.isNull())
HANDLE_NULL_INPUT;
if (sh.isNull()) {
FC_THROWM(NullShapeException, "Null input shape");
}
auto shape = sh.getShape();
// Allow compounds with a single face, wire or vertex or
// if there are only edges building one wire
if (shape.ShapeType() == TopAbs_COMPOUND) {
sh = sh.makEWires();
if(sh.isNull())
HANDLE_NULL_INPUT;
sh = sh.makeElementWires();
if (sh.isNull()) {
FC_THROWM(NullShapeException, "Null input shape");
}
shape = sh.getShape();
}
if (shape.ShapeType() == TopAbs_FACE) {
shape = sh.splitWires().getShape();
} else if (shape.ShapeType() == TopAbs_WIRE) {
}
else if (shape.ShapeType() == TopAbs_WIRE) {
// do nothing
} else if (shape.ShapeType() == TopAbs_EDGE) {
}
else if (shape.ShapeType() == TopAbs_EDGE) {
BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(shape));
shape = mkWire.Wire();
} else if (shape.ShapeType() != TopAbs_VERTEX) {
FC_THROWM(Base::CADKernelError,"Profile shape is not a vertex, edge, wire nor face.");
}
else if (shape.ShapeType() != TopAbs_VERTEX) {
FC_THROWM(Base::CADKernelError, "Profile shape is not a vertex, edge, wire nor face.");
}
ret.push_back(shape);
}
if(ret.empty())
FC_THROWM(Base::CADKernelError,"No profile");
if (ret.empty()) {
FC_THROWM(Base::CADKernelError, "No profile");
}
return ret;
}
@@ -2340,60 +2348,71 @@ TopoShape& TopoShape::makeElementShape(BRepPrimAPI_MakeHalfSpace& mkShape,
return makeShapeWithElementMap(mkShape.Solid(), MapperMaker(mkShape), {source}, op);
}
TopoShape &TopoShape::makeElementLoft(const std::vector<TopoShape> &shapes,
Standard_Boolean isSolid,
Standard_Boolean isRuled,
Standard_Boolean isClosed,
Standard_Integer maxDegree,
const char *op)
TopoShape& TopoShape::makeElementLoft(const std::vector<TopoShape>& shapes,
IsSolid isSolid,
IsRuled isRuled,
IsClosed isClosed,
Standard_Integer maxDegree,
const char* op)
{
if(!op) op = Part::OpCodes::Loft;
if (!op) {
op = Part::OpCodes::Loft;
}
// http://opencascade.blogspot.com/2010/01/surface-modeling-part5.html
BRepOffsetAPI_ThruSections aGenerator (isSolid,isRuled);
BRepOffsetAPI_ThruSections aGenerator(isSolid == IsSolid::solid, isRuled == IsRuled::ruled);
aGenerator.SetMaxDegree(maxDegree);
auto profiles = prepareProfiles(shapes);
if (shapes.size() < 2)
FC_THROWM(Base::CADKernelError,"Need at least two vertices, edges or wires to create loft face");
if (shapes.size() < 2) {
FC_THROWM(Base::CADKernelError,
"Need at least two vertices, edges or wires to create loft face");
}
for(auto &sh : profiles) {
const auto &shape = sh.getShape();
if(shape.ShapeType() == TopAbs_VERTEX)
aGenerator.AddVertex(TopoDS::Vertex (shape));
else
aGenerator.AddWire(TopoDS::Wire (shape));
for (auto& sh : profiles) {
const auto& shape = sh.getShape();
if (shape.ShapeType() == TopAbs_VERTEX) {
aGenerator.AddVertex(TopoDS::Vertex(shape));
}
else {
aGenerator.AddWire(TopoDS::Wire(shape));
}
}
// close loft by duplicating initial profile as last profile. not perfect.
if (isClosed) {
if (isClosed == IsClosed::closed) {
/* can only close loft in certain combinations of Vertex/Wire(Edge):
- V1-W1-W2-W3-V2 ==> V1-W1-W2-W3-V2-V1 invalid closed
- V1-W1-W2-W3 ==> V1-W1-W2-W3-V1 valid closed
- W1-W2-W3-V1 ==> W1-W2-W3-V1-W1 invalid closed
- W1-W2-W3 ==> W1-W2-W3-W1 valid closed*/
if (profiles.back().getShape().ShapeType() == TopAbs_VERTEX) {
Base::Console().Message("TopoShape::makeLoft: can't close Loft with Vertex as last profile. 'Closed' ignored.\n");
Base::Console().Message("TopoShape::makeLoft: can't close Loft with Vertex as last "
"profile. 'Closed' ignored.\n");
}
else {
// repeat Add logic above for first profile
const TopoDS_Shape& firstProfile = profiles.front().getShape();
if (firstProfile.ShapeType() == TopAbs_VERTEX) {
aGenerator.AddVertex(TopoDS::Vertex (firstProfile));
if (firstProfile.ShapeType() == TopAbs_VERTEX) {
aGenerator.AddVertex(TopoDS::Vertex(firstProfile));
}
else if (firstProfile.ShapeType() == TopAbs_EDGE) {
else if (firstProfile.ShapeType() == TopAbs_EDGE) {
aGenerator.AddWire(BRepBuilderAPI_MakeWire(TopoDS::Edge(firstProfile)).Wire());
}
else if (firstProfile.ShapeType() == TopAbs_WIRE) {
aGenerator.AddWire(TopoDS::Wire (firstProfile));
else if (firstProfile.ShapeType() == TopAbs_WIRE) {
aGenerator.AddWire(TopoDS::Wire(firstProfile));
}
}
}
Standard_Boolean anIsCheck = Standard_True;
aGenerator.CheckCompatibility (anIsCheck); // use BRepFill_CompatibleWires on profiles. force #edges, orientation, "origin" to match.
aGenerator.CheckCompatibility(anIsCheck); // use BRepFill_CompatibleWires on profiles. force
// #edges, orientation, "origin" to match.
aGenerator.Build();
return makeShapeWithElementMap(aGenerator.Shape(),MapperThruSections(aGenerator,profiles),shapes,op);
return makeShapeWithElementMap(aGenerator.Shape(),
MapperThruSections(aGenerator, profiles),
shapes,
op);
}
TopoShape& TopoShape::makeElementDraft(const TopoShape& shape,

View File

@@ -1,8 +1,5 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
#include <BRepPrimAPI_MakeBox.hxx>
#include <TopExp_Explorer.hxx>
#include "PartTestHelpers.h"
// NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)

View File

@@ -9,12 +9,15 @@
#include "Mod/Part/App/FeaturePartBox.h"
#include "Mod/Part/App/FeaturePartFuse.h"
#include "Mod/Part/App/FeatureFillet.h"
#include <BRepGProp.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepGProp.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <GC_MakeCircle.hxx>
#include <TopoDS.hxx>
#include <TopExp_Explorer.hxx>
namespace PartTestHelpers
{

View File

@@ -1185,4 +1185,48 @@ TEST_F(TopoShapeExpansionTest, makeElementRuledSurfaceWires)
EXPECT_FLOAT_EQ(getArea(result.getShape()), 2.023056);
}
TEST_F(TopoShapeExpansionTest, makeElementLoft)
{
// Loft must have either all open or all closed sections to work, we'll do two closed.
// Arrange
const float Len = 5;
const float Wid = 5;
auto [face1, wire1, edge1, edge2, edge3, edge4] = CreateRectFace(Len, Wid);
auto transform {gp_Trsf()};
transform.SetTranslation(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(0.0, 0.0, 10.0));
auto wire2 = wire1; // Shallow copy
wire2.Move(TopLoc_Location(transform));
TopoShape wire1ts {wire1,
1L}; // One of these shapes should have a tag or we won't get an Element Map
TopoShape wire2ts {
wire2,
2L}; // If you change either tag or eliminate one it changes the resulting name.
std::vector<TopoShape> shapes = {wire1ts, wire2ts};
// Act
auto& topoShape =
(new TopoShape())->makeElementLoft(shapes, IsSolid::notSolid, IsRuled::notRuled);
auto& topoShape2 =
(new TopoShape())->makeElementLoft(shapes, IsSolid::solid, IsRuled::notRuled);
auto& topoShape3 =
(new TopoShape())->makeElementLoft(shapes, IsSolid::notSolid, IsRuled::ruled);
auto& topoShape4 = (new TopoShape())->makeElementLoft(shapes, IsSolid::solid, IsRuled::ruled);
auto& topoShape5 =
(new TopoShape())
->makeElementLoft(shapes, IsSolid::notSolid, IsRuled::notRuled, IsClosed::closed);
auto elements = elementMap((topoShape));
// Assert that we haven't broken the basic Loft functionality
EXPECT_EQ(topoShape.countSubElements("Wire"), 4);
EXPECT_FLOAT_EQ(getArea(topoShape.getShape()), 200);
EXPECT_FLOAT_EQ(getVolume(topoShape.getShape()), 166.66667);
EXPECT_FLOAT_EQ(getVolume(topoShape2.getShape()), 250);
EXPECT_FLOAT_EQ(getVolume(topoShape3.getShape()), 166.66667);
EXPECT_FLOAT_EQ(getVolume(topoShape4.getShape()), 250);
EXPECT_NEAR(getVolume(topoShape5.getShape()), 0, 1e-07);
// Assert that we're creating a correct element map
EXPECT_TRUE(topoShape.getMappedChildElements().empty());
EXPECT_EQ(elements.size(), 24);
EXPECT_EQ(elements.count(IndexedName("Edge", 1)), 1);
EXPECT_EQ(elements[IndexedName("Edge", 1)], MappedName("Edge1;LFT;:H1:4,E"));
}
// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)

View File

@@ -8,8 +8,6 @@
#include "PartTestHelpers.h"
#include <Mod/Part/App/TopoShape.h>
#include <BRepBuilderAPI_MakeVertex.hxx>
using namespace Data;
using namespace Part;
using namespace PartTestHelpers;