Merge pull request #12431 from bgbsww/bgbsww-toponamingMakeElementRuledSurface

Toponaming/Part:  make element ruled surface
This commit is contained in:
Chris Hennes
2024-02-15 20:55:29 -06:00
committed by GitHub
4 changed files with 225 additions and 0 deletions

View File

@@ -114,6 +114,27 @@ App::DocumentObjectExecReturn* RuledSurface::getShape(const App::PropertyLinkSub
App::DocumentObjectExecReturn *RuledSurface::execute()
{
try {
#ifdef FC_USE_TNP_FIX
std::vector<TopoShape> shapes;
std::array<App::PropertyLinkSub*,2> links = {&Curve1,&Curve2};
for(auto link : links) {
const auto &subs = link->getSubValues();
if(subs.empty())
shapes.push_back(getTopoShape(link->getValue()));
else if(subs.size()!=1)
return new App::DocumentObjectExecReturn("Not exactly one sub-shape linked.");
else
shapes.push_back(getTopoShape(link->getValue(),
subs.front().c_str(),true));
if(shapes.back().isNull())
return new App::DocumentObjectExecReturn("Invalid link.");
}
TopoShape res(0);//, getDocument()->getStringHasher());
res.makeElementRuledSurface(shapes, Orientation.getValue());
this->Shape.setValue(res);
return Part::Feature::execute();
#else
App::DocumentObjectExecReturn* ret;
// get the first input shape
@@ -231,6 +252,7 @@ App::DocumentObjectExecReturn *RuledSurface::execute()
this->Shape.setValue(ruledShape);
return App::DocumentObject::StdReturn;
#endif
}
catch (Standard_Failure& e) {

View File

@@ -786,6 +786,21 @@ public:
}
};
/** Make a ruled surface
*
* @param sources: the source shapes, each of which must contain either a
* single edge or a single wire.
* @param orientation: A Qt::Orientation, where Qt::Horizontal is 1 and Qt::Vertical is 2.
* @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 &makeElementRuledSurface(const std::vector<TopoShape> &source, int orientation=0, const char *op=nullptr);
/** Core function to generate mapped element names from shape history
*
* @param shape: the new shape

View File

@@ -27,9 +27,16 @@
#ifndef _PreComp_
#include <cmath>
#include <BRepAdaptor_Curve.hxx>
#include <BRepAdaptor_CompCurve.hxx>
# if OCC_VERSION_HEX < 0x070600
# include <BRepAdaptor_HCurve.hxx>
# include <BRepAdaptor_HCompCurve.hxx>
# endif
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepCheck_Analyzer.hxx>
#include <BRepFill.hxx>
#include <BRepFill_Generator.hxx>
#include <BRepTools.hxx>
#include <BRep_Builder.hxx>
@@ -77,6 +84,12 @@
FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT
#if OCC_VERSION_HEX >= 0x070600
using Adaptor3d_HCurve = Adaptor3d_Curve;
using BRepAdaptor_HCurve = BRepAdaptor_Curve;
using BRepAdaptor_HCompCurve = BRepAdaptor_CompCurve;
#endif
namespace Part
{
@@ -1653,6 +1666,154 @@ TopoShape TopoShape::getSubTopoShape(TopAbs_ShapeEnum type, int idx, bool silent
return shapeMap.getTopoShape(*this, idx);
}
TopoShape& TopoShape::makeElementRuledSurface(const std::vector<TopoShape>& shapes,
int orientation,
const char* op)
{
if (!op) {
op = Part::OpCodes::RuledSurface;
}
if (shapes.size() != 2) {
FC_THROWM(Base::CADKernelError, "Wrong number of input shapes");
}
std::vector<TopoShape> curves(2);
int i = 0;
for (auto& s : shapes) {
if (s.isNull()) {
FC_THROWM(NullShapeException, "Null input shape");
}
auto type = s.shapeType();
if (type == TopAbs_WIRE || type == TopAbs_EDGE) {
curves[i++] = s;
continue;
}
auto countOfWires = s.countSubShapes(TopAbs_WIRE);
if (countOfWires > 1) {
FC_THROWM(Base::CADKernelError, "Input shape has more than one wire");
}
if (countOfWires == 1) {
curves[i++] = s.getSubTopoShape(TopAbs_WIRE, 1);
continue;
}
auto countOfEdges = s.countSubShapes(TopAbs_EDGE);
if (countOfEdges == 0) {
FC_THROWM(Base::CADKernelError, "Input shape has no edge");
}
if (countOfEdges == 1) {
curves[i++] = s.getSubTopoShape(TopAbs_EDGE, 1);
continue;
}
curves[i] = s.makeElementWires();
if (curves[i].isNull()) {
FC_THROWM(NullShapeException, "Null input shape");
}
if (curves[i].shapeType() != TopAbs_WIRE) {
FC_THROWM(Base::CADKernelError, "Input shape forms more than one wire");
}
++i;
}
if (curves[0].shapeType() != curves[1].shapeType()) {
for (auto& curve : curves) {
if (curve.shapeType() == TopAbs_EDGE) {
curve = curve.makeElementWires();
}
}
}
auto& S1 = curves[0];
auto& S2 = curves[1];
bool isWire = S1.shapeType() == TopAbs_WIRE;
// https://forum.freecadweb.org/viewtopic.php?f=8&t=24052
//
// if both shapes are sub-elements of one common shape then the fill
// algorithm leads to problems if the shape has set a placement. The
// workaround is to copy the sub-shape
S1 = S1.makeElementCopy();
S2 = S2.makeElementCopy();
if (orientation == 0) {
// Automatic
Handle(Adaptor3d_HCurve) a1;
Handle(Adaptor3d_HCurve) a2;
if (!isWire) {
BRepAdaptor_HCurve adapt1(TopoDS::Edge(S1.getShape()));
BRepAdaptor_HCurve adapt2(TopoDS::Edge(S2.getShape()));
a1 = new BRepAdaptor_HCurve(adapt1);
a2 = new BRepAdaptor_HCurve(adapt2);
}
else {
BRepAdaptor_HCompCurve adapt1(TopoDS::Wire(S1.getShape()));
BRepAdaptor_HCompCurve adapt2(TopoDS::Wire(S2.getShape()));
a1 = new BRepAdaptor_HCompCurve(adapt1);
a2 = new BRepAdaptor_HCompCurve(adapt2);
}
if (!a1.IsNull() && !a2.IsNull()) {
// get end points of 1st curve
gp_Pnt p1 = a1->Value(a1->FirstParameter());
gp_Pnt p2 = a1->Value(a1->LastParameter());
if (S1.getShape().Orientation() == TopAbs_REVERSED) {
std::swap(p1, p2);
}
// get end points of 2nd curve
gp_Pnt p3 = a2->Value(a2->FirstParameter());
gp_Pnt p4 = a2->Value(a2->LastParameter());
if (S2.getShape().Orientation() == TopAbs_REVERSED) {
std::swap(p3, p4);
}
// Form two triangles (P1,P2,P3) and (P4,P3,P2) and check their normals.
// If the dot product is negative then it's assumed that the resulting face
// is twisted, hence the 2nd edge is reversed.
gp_Vec v1(p1, p2);
gp_Vec v2(p1, p3);
gp_Vec n1 = v1.Crossed(v2);
gp_Vec v3(p4, p3);
gp_Vec v4(p4, p2);
gp_Vec n2 = v3.Crossed(v4);
if (n1.Dot(n2) < 0) {
S2.setShape(S2.getShape().Reversed(), false);
}
}
}
else if (orientation == 2) {
// Reverse
S2.setShape(S2.getShape().Reversed(), false);
}
TopoDS_Shape ruledShape;
if (!isWire) {
ruledShape = BRepFill::Face(TopoDS::Edge(S1.getShape()), TopoDS::Edge(S2.getShape()));
}
else {
ruledShape = BRepFill::Shell(TopoDS::Wire(S1.getShape()), TopoDS::Wire(S2.getShape()));
}
// Both BRepFill::Face() and Shell() modifies the original input edges
// without any API to provide relationship to the output edges. So we have
// to use searchSubShape() to build the relationship by ourselves.
TopoShape res(ruledShape.Located(TopLoc_Location()));
std::vector<TopoShape> edges;
for (const auto& c : curves) {
for (const auto& e : c.getSubTopoShapes(TopAbs_EDGE)) {
auto found = res.findSubShapesWithSharedVertex(e);
if (found.size() > 0) {
found.front().resetElementMap(e.elementMap());
edges.push_back(found.front());
}
}
}
// Use empty mapper and let makeShapeWithElementMap name the created surface with lower elements.
return makeShapeWithElementMap(res.getShape(), Mapper(), edges, op);
}
TopoShape& TopoShape::makeElementCompound(const std::vector<TopoShape>& shapes,
const char* op,

View File

@@ -1157,5 +1157,32 @@ TEST_F(TopoShapeExpansionTest, makeElementLinearizeFace)
EXPECT_EQ(surface2.GetType(), GeomAbs_Plane);
}
TEST_F(TopoShapeExpansionTest, makeElementRuledSurfaceEdges)
{
// Arrange
auto [cube1, cube2] = CreateTwoCubes();
TopoShape cube1TS {cube1, 1L};
std::vector<TopoShape> subEdges = cube1TS.getSubTopoShapes(TopAbs_EDGE);
std::vector<TopoShape> shapes2 = {subEdges[0], subEdges[1]};
// Act
TopoShape result2 = cube1TS.makeElementRuledSurface(shapes2, 0); // TODO: direction as enum?
// Assert
EXPECT_EQ(result2.countSubElements("Wire"), 1);
EXPECT_FLOAT_EQ(getArea(result2.getShape()), 0.32953611);
}
TEST_F(TopoShapeExpansionTest, makeElementRuledSurfaceWires)
{
// Arrange
auto [cube1, cube2] = CreateTwoCubes();
TopoShape cube1TS {cube1, 1L};
std::vector<TopoShape> subWires = cube1TS.getSubTopoShapes(TopAbs_WIRE);
std::vector<TopoShape> shapes = {subWires[0], subWires[1]};
// Act
TopoShape result = cube1TS.makeElementRuledSurface(shapes, 0); // TODO: direction as enum?
// Assert
EXPECT_EQ(result.countSubElements("Wire"), 4);
EXPECT_FLOAT_EQ(getArea(result.getShape()), 2.023056);
}
// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)