Merge pull request #12590 from bgbsww/bgbsww-toponamingMakeElementBSplineFace
Toponaming/Part make element b spline face and filled face
This commit is contained in:
@@ -226,6 +226,16 @@ enum class Copy
|
||||
copy
|
||||
};
|
||||
|
||||
/// Filling style when making a BSpline face
|
||||
enum 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,
|
||||
};
|
||||
|
||||
/** The representation for a CAD Shape
|
||||
*/
|
||||
// NOLINTNEXTLINE cppcoreguidelines-special-member-functions
|
||||
@@ -1942,16 +1952,64 @@ public:
|
||||
return TopoShape(0, Hasher).makeElementFace(*this, op, maker, plane);
|
||||
}
|
||||
|
||||
/// Filling style when making a BSpline face
|
||||
enum class FillingStyle
|
||||
/** Make a face with BSpline (or Bezier) surface
|
||||
*
|
||||
* @param shapes: input shapes of any type, but only edges inside the shape
|
||||
* will be used.
|
||||
* @param style: surface filling style. @sa FillingStyle
|
||||
* @param keepBezier: whether to create Bezier surface if the input edge
|
||||
* has Bezier curve.
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
*
|
||||
* @return The function creates a face with either BSpline or Bezier
|
||||
* surface. 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 &makeElementBSplineFace(const std::vector<TopoShape> &input,
|
||||
FillingStyle style = FillingStyle::stretch,
|
||||
bool keepBezier = false,
|
||||
const char *op=nullptr);
|
||||
/** Make a face with BSpline (or Bezier) surface
|
||||
*
|
||||
* @param shape: input shape of any type, but only edges inside the shape
|
||||
* will be used.
|
||||
* @param style: surface filling style. @sa FillingStyle
|
||||
* @param keepBezier: whether to create Bezier surface if the input edge
|
||||
* has Bezier curve.
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
*
|
||||
* @return The function creates a face with either BSpline or Bezier
|
||||
* surface. 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 &makeElementBSplineFace(const TopoShape &input,
|
||||
FillingStyle style = FillingStyle::stretch,
|
||||
bool keepBezier = false,
|
||||
const char *op=nullptr);
|
||||
/** Make a face with BSpline (or Bezier) surface
|
||||
*
|
||||
* @param style: surface filling style. @sa FillingStyle
|
||||
* @param keepBezier: whether to create Bezier surface if the input edge
|
||||
* has Bezier curve.
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
*
|
||||
* @return The function returns a new face with either BSpline or Bezier
|
||||
* surface. The shape itself is not modified.
|
||||
*/
|
||||
TopoShape makeElementBSplineFace(FillingStyle style = FillingStyle::stretch,
|
||||
bool keepBezier = false,
|
||||
const char *op=nullptr)
|
||||
{
|
||||
/// 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,
|
||||
};
|
||||
return TopoShape(0,Hasher).makeElementBSplineFace(*this, style, keepBezier, op);
|
||||
}
|
||||
|
||||
|
||||
struct BRepFillingParams;
|
||||
|
||||
@@ -1988,6 +2046,29 @@ public:
|
||||
CN,
|
||||
};
|
||||
|
||||
/** Make a non-planar filled face with boundary and/or constraint edge/wire
|
||||
*
|
||||
* @param shapes: input shapes of any type. The function will automatically
|
||||
* discover connected and closed edges to be used as the
|
||||
* boundary of the the new face. Any other vertex, edge,
|
||||
* and/or face will be used as constraints to fine tune the
|
||||
* surface generation.
|
||||
* @param params: @sa BRepFillingParams
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
*
|
||||
* @return The function creates a face with BSpline surface. 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.
|
||||
*
|
||||
* @sa OCCT BRepOffsetAPI_MakeFilling
|
||||
*/
|
||||
TopoShape &makeElementFilledFace(const std::vector<TopoShape> &shapes,
|
||||
const BRepFillingParams ¶ms,
|
||||
const char *op=nullptr);
|
||||
|
||||
/** Make a solid using shells or CompSolid
|
||||
*
|
||||
* @param shapes: input shapes of either shells or CompSolid.
|
||||
|
||||
@@ -50,24 +50,35 @@
|
||||
#include <BRepBuilderAPI_Copy.hxx>
|
||||
#include <BRepBuilderAPI_GTransform.hxx>
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepBuilderAPI_NurbsConvert.hxx>
|
||||
#include <BRepBuilderAPI_Transform.hxx>
|
||||
#include <BRepBuilderAPI_MakeSolid.hxx>
|
||||
#include <BRepBuilderAPI_NurbsConvert.hxx>
|
||||
#include <BRepFilletAPI_MakeChamfer.hxx>
|
||||
#include <BRepFilletAPI_MakeFillet.hxx>
|
||||
#include <BRepBuilderAPI_Transform.hxx>
|
||||
#include <BRepLib.hxx>
|
||||
#include <BRepOffsetAPI_DraftAngle.hxx>
|
||||
#include <BRepOffsetAPI_MakeFilling.hxx>
|
||||
#include <BRepOffsetAPI_MakePipe.hxx>
|
||||
#include <BRepOffsetAPI_MakeThickSolid.hxx>
|
||||
#include <BRepPrimAPI_MakeRevol.hxx>
|
||||
#include <BRepPrimAPI_MakePrism.hxx>
|
||||
#include <BRepProj_Projection.hxx>
|
||||
#include <BRepTools_WireExplorer.hxx>
|
||||
#include <GeomConvert.hxx>
|
||||
#include <GeomFill_BezierCurves.hxx>
|
||||
#include <GeomFill_BSplineCurves.hxx>
|
||||
#include <Precision.hxx>
|
||||
#include <ShapeAnalysis_FreeBounds.hxx>
|
||||
#include <ShapeBuild_ReShape.hxx>
|
||||
#include <ShapeConstruct_Curve.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 <TopTools_HSequenceOfShape.hxx>
|
||||
#include <gp_Pln.hxx>
|
||||
|
||||
#include <utility>
|
||||
@@ -88,10 +99,6 @@
|
||||
#include "Geometry.h"
|
||||
|
||||
#include <App/ElementNamingUtils.h>
|
||||
#include <BRepPrimAPI_MakeRevol.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepPrimAPI_MakePrism.hxx>
|
||||
#include <BRepProj_Projection.hxx>
|
||||
|
||||
FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT
|
||||
|
||||
@@ -2804,6 +2811,196 @@ struct MapperPrism: MapperMaker {
|
||||
}
|
||||
};
|
||||
|
||||
TopoShape& TopoShape::makeElementFilledFace(const std::vector<TopoShape>& _shapes,
|
||||
const BRepFillingParams& params,
|
||||
const char* op)
|
||||
{
|
||||
if (!op) {
|
||||
op = Part::OpCodes::FilledFace;
|
||||
}
|
||||
BRepOffsetAPI_MakeFilling maker(params.degree,
|
||||
params.ptsoncurve,
|
||||
params.numiter,
|
||||
params.anisotropy,
|
||||
params.tol2d,
|
||||
params.tol3d,
|
||||
params.tolG1,
|
||||
params.tolG2,
|
||||
params.maxdeg,
|
||||
params.maxseg);
|
||||
|
||||
if (!params.surface.isNull() && params.surface.getShape().ShapeType() == TopAbs_FACE) {
|
||||
maker.LoadInitSurface(TopoDS::Face(params.surface.getShape()));
|
||||
}
|
||||
|
||||
std::vector<TopoShape> shapes;
|
||||
for (auto& s : _shapes) {
|
||||
expandCompound(s, shapes);
|
||||
}
|
||||
|
||||
TopoShapeMap output;
|
||||
auto getOrder = [&](const TopoDS_Shape& s) {
|
||||
auto it = params.orders.find(s);
|
||||
if (it == params.orders.end()) {
|
||||
auto iter = output.find(s);
|
||||
if (iter != output.end()) {
|
||||
it = params.orders.find(iter->second.getShape());
|
||||
}
|
||||
}
|
||||
if (it != params.orders.end()) {
|
||||
return static_cast<GeomAbs_Shape>(it->second);
|
||||
}
|
||||
return GeomAbs_C0;
|
||||
};
|
||||
|
||||
auto getSupport = [&](const TopoDS_Shape& s) {
|
||||
TopoDS_Face support;
|
||||
auto it = params.supports.find(s);
|
||||
if (it == params.supports.end()) {
|
||||
auto iter = output.find(s);
|
||||
if (iter != output.end()) {
|
||||
it = params.supports.find(iter->second.getShape());
|
||||
}
|
||||
}
|
||||
if (it != params.supports.end()) {
|
||||
if (!it->second.IsNull() && it->second.ShapeType() == TopAbs_FACE) {
|
||||
support = TopoDS::Face(it->second);
|
||||
}
|
||||
}
|
||||
return support;
|
||||
};
|
||||
|
||||
auto findBoundary = [](std::vector<TopoShape>& shapes) -> TopoShape {
|
||||
// Find a wire (preferably a closed one) to be used as the boundary.
|
||||
int i = -1;
|
||||
int boundIdx = -1;
|
||||
for (auto& s : shapes) {
|
||||
++i;
|
||||
if (s.isNull() || !s.hasSubShape(TopAbs_EDGE) || s.shapeType() != TopAbs_WIRE) {
|
||||
continue;
|
||||
}
|
||||
if (BRep_Tool::IsClosed(TopoDS::Wire(s.getShape()))) {
|
||||
boundIdx = i;
|
||||
break;
|
||||
}
|
||||
else if (boundIdx < 0) {
|
||||
boundIdx = i;
|
||||
}
|
||||
}
|
||||
if (boundIdx >= 0) {
|
||||
auto res = shapes[boundIdx];
|
||||
shapes.erase(shapes.begin() + boundIdx);
|
||||
return res;
|
||||
}
|
||||
return TopoShape();
|
||||
};
|
||||
|
||||
TopoShape bound;
|
||||
std::vector<TopoShape> wires;
|
||||
if (params.boundary_begin >= 0 && params.boundary_end > params.boundary_begin
|
||||
&& params.boundary_end <= (int)shapes.size()) {
|
||||
if (params.boundary_end - 1 != params.boundary_begin
|
||||
|| shapes[params.boundary_begin].shapeType() != TopAbs_WIRE) {
|
||||
std::vector<TopoShape> edges;
|
||||
edges.insert(edges.end(),
|
||||
shapes.begin() + params.boundary_begin,
|
||||
shapes.begin() + params.boundary_end);
|
||||
wires = TopoShape(0, Hasher)
|
||||
.makeElementWires(edges,
|
||||
"",
|
||||
0.0,
|
||||
ConnectionPolicy::requireSharedVertex,
|
||||
&output)
|
||||
.getSubTopoShapes(TopAbs_WIRE);
|
||||
shapes.erase(shapes.begin() + params.boundary_begin,
|
||||
shapes.begin() + params.boundary_end);
|
||||
}
|
||||
}
|
||||
else {
|
||||
bound = findBoundary(shapes);
|
||||
if (bound.isNull()) {
|
||||
// If no boundary is found, then try to build one.
|
||||
std::vector<TopoShape> edges;
|
||||
for (auto it = shapes.begin(); it != shapes.end();) {
|
||||
if (it->shapeType(true) == TopAbs_EDGE) {
|
||||
edges.push_back(*it);
|
||||
it = shapes.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (edges.size()) {
|
||||
wires = TopoShape(0, Hasher)
|
||||
.makeElementWires(edges,
|
||||
"",
|
||||
0.0,
|
||||
ConnectionPolicy::requireSharedVertex,
|
||||
&output)
|
||||
.getSubTopoShapes(TopAbs_WIRE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bound.isNull()) {
|
||||
bound = findBoundary(wires);
|
||||
}
|
||||
|
||||
if (bound.isNull()) {
|
||||
FC_THROWM(Base::CADKernelError, "No boundary wire");
|
||||
}
|
||||
|
||||
// Since we've only selected one wire for boundary, return all the
|
||||
// other edges in shapes to be added as non boundary constraints
|
||||
shapes.insert(shapes.end(), wires.begin(), wires.end());
|
||||
|
||||
// Must fix wire connection to avoid OCC crash in BRepFill_Filling.cxx WireFromList()
|
||||
// https://github.com/Open-Cascade-SAS/OCCT/blob/1c96596ae7ba120a678021db882857e289c73947/src/BRepFill/BRepFill_Filling.cxx#L133
|
||||
// The reason of crash is because the wire connection tolerance is too big.
|
||||
// The crash can be fixed by simply checking itl.More() before calling Remove().
|
||||
bound.fix(Precision::Confusion(), Precision::Confusion(), Precision::Confusion());
|
||||
|
||||
for (const auto& e : bound.getOrderedEdges()) {
|
||||
maker.Add(TopoDS::Edge(e.getShape()),
|
||||
getSupport(e.getShape()),
|
||||
getOrder(e.getShape()),
|
||||
/*IsBound*/ Standard_True);
|
||||
}
|
||||
|
||||
for (const auto& s : shapes) {
|
||||
if (s.isNull()) {
|
||||
continue;
|
||||
}
|
||||
const auto& sh = s.getShape();
|
||||
if (sh.ShapeType() == TopAbs_WIRE) {
|
||||
for (const auto& e : s.getSubShapes(TopAbs_EDGE)) {
|
||||
maker.Add(TopoDS::Edge(e),
|
||||
getSupport(e),
|
||||
getOrder(e),
|
||||
/*IsBound*/ Standard_False);
|
||||
}
|
||||
}
|
||||
else if (sh.ShapeType() == TopAbs_EDGE) {
|
||||
maker.Add(TopoDS::Edge(sh),
|
||||
getSupport(sh),
|
||||
getOrder(sh),
|
||||
/*IsBound*/ Standard_False);
|
||||
}
|
||||
else if (sh.ShapeType() == TopAbs_FACE) {
|
||||
maker.Add(TopoDS::Face(sh), getOrder(sh));
|
||||
}
|
||||
else if (sh.ShapeType() == TopAbs_VERTEX) {
|
||||
maker.Add(BRep_Tool::Pnt(TopoDS::Vertex(sh)));
|
||||
}
|
||||
}
|
||||
|
||||
maker.Build();
|
||||
if (!maker.IsDone()) {
|
||||
FC_THROWM(Base::CADKernelError, "Failed to created face by filling edges");
|
||||
}
|
||||
return makeElementShape(maker, _shapes, op);
|
||||
}
|
||||
|
||||
// TODO: This method does not appear to ever be called in the codebase, and it is probably
|
||||
// broken, because using TopoShape() with no parameters means the result will not have an
|
||||
// element Map.
|
||||
@@ -3554,6 +3751,241 @@ TopoShape& TopoShape::makeElementRefine(const TopoShape& shape, const char* op,
|
||||
return *this;
|
||||
}
|
||||
|
||||
TopoShape& TopoShape::makeElementBSplineFace(const TopoShape& shape,
|
||||
FillingStyle style,
|
||||
bool keepBezier,
|
||||
const char* op)
|
||||
{
|
||||
std::vector<TopoShape> input(1, shape);
|
||||
return makeElementBSplineFace(input, style, keepBezier, op);
|
||||
}
|
||||
|
||||
TopoShape& TopoShape::makeElementBSplineFace(const std::vector<TopoShape>& input,
|
||||
FillingStyle style,
|
||||
bool keepBezier,
|
||||
const char* op)
|
||||
{
|
||||
std::vector<TopoShape> edges;
|
||||
for (auto& s : input) {
|
||||
auto e = s.getSubTopoShapes(TopAbs_EDGE);
|
||||
edges.insert(edges.end(), e.begin(), e.end());
|
||||
}
|
||||
|
||||
if (edges.size() == 1 && edges[0].isClosed()) {
|
||||
auto edge = edges[0].getSubShape(TopAbs_EDGE, 1);
|
||||
auto e = TopoDS::Edge(edge);
|
||||
auto v = TopExp::FirstVertex(e);
|
||||
Standard_Real first, last;
|
||||
Handle(Geom_Curve) curve = BRep_Tool::Curve(e, first, last);
|
||||
|
||||
BRepBuilderAPI_MakeEdge mk1, mk2, mk3, mk4;
|
||||
Handle(Geom_BSplineCurve) bspline = Handle(Geom_BSplineCurve)::DownCast(curve);
|
||||
if (bspline.IsNull()) {
|
||||
ShapeConstruct_Curve scc;
|
||||
bspline = scc.ConvertToBSpline(curve, first, last, Precision::Confusion());
|
||||
if (bspline.IsNull()) {
|
||||
FC_THROWM(Base::CADKernelError, "Failed to convert edge to bspline");
|
||||
}
|
||||
first = bspline->FirstParameter();
|
||||
last = bspline->LastParameter();
|
||||
}
|
||||
auto step = (last - first) * 0.25;
|
||||
auto m1 = first + step;
|
||||
auto m2 = m1 + step;
|
||||
auto m3 = m2 + step;
|
||||
auto c1 = GeomConvert::SplitBSplineCurve(bspline, first, m1, Precision::Confusion());
|
||||
auto c2 = GeomConvert::SplitBSplineCurve(bspline, m1, m2, Precision::Confusion());
|
||||
auto c3 = GeomConvert::SplitBSplineCurve(bspline, m2, m3, Precision::Confusion());
|
||||
auto c4 = GeomConvert::SplitBSplineCurve(bspline, m3, last, Precision::Confusion());
|
||||
mk1.Init(c1);
|
||||
mk2.Init(c2);
|
||||
mk3.Init(c3);
|
||||
mk4.Init(c4);
|
||||
|
||||
if (!mk1.IsDone() || !mk2.IsDone() || !mk3.IsDone() || !mk4.IsDone()) {
|
||||
FC_THROWM(Base::CADKernelError, "Failed to split edge");
|
||||
}
|
||||
|
||||
auto e1 = mk1.Edge();
|
||||
auto e2 = mk2.Edge();
|
||||
auto e3 = mk3.Edge();
|
||||
auto e4 = mk4.Edge();
|
||||
|
||||
ShapeMapper mapper;
|
||||
mapper.populate(MappingStatus::Modified, e, {e1, e2, e3, e4});
|
||||
mapper.populate(MappingStatus::Generated, v, {TopExp::FirstVertex(e1)});
|
||||
mapper.populate(MappingStatus::Generated, v, {TopExp::LastVertex(e4)});
|
||||
|
||||
BRep_Builder builder;
|
||||
TopoDS_Compound comp;
|
||||
builder.MakeCompound(comp);
|
||||
builder.Add(comp, e1);
|
||||
builder.Add(comp, e2);
|
||||
builder.Add(comp, e3);
|
||||
builder.Add(comp, e4);
|
||||
|
||||
TopoShape s;
|
||||
s.makeShapeWithElementMap(comp, mapper, edges, Part::OpCodes::Split);
|
||||
return makeElementBSplineFace(s, style, op);
|
||||
}
|
||||
|
||||
if (edges.size() < 2 || edges.size() > 4) {
|
||||
FC_THROWM(Base::CADKernelError, "Require minimum two, maximum four edges");
|
||||
}
|
||||
|
||||
GeomFill_FillingStyle fstyle;
|
||||
switch (style) {
|
||||
case coons:
|
||||
fstyle = GeomFill_CoonsStyle;
|
||||
break;
|
||||
case curved:
|
||||
fstyle = GeomFill_CurvedStyle;
|
||||
break;
|
||||
default:
|
||||
fstyle = GeomFill_StretchStyle;
|
||||
}
|
||||
|
||||
Handle(Geom_Surface) aSurface;
|
||||
|
||||
Standard_Real u1, u2;
|
||||
if (keepBezier) {
|
||||
std::vector<Handle(Geom_BezierCurve)> curves;
|
||||
curves.reserve(4);
|
||||
for (const auto& e : edges) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(e.getShape());
|
||||
TopLoc_Location heloc; // this will be output
|
||||
Handle(Geom_Curve) c_geom = BRep_Tool::Curve(edge, heloc, u1, u2);
|
||||
Handle(Geom_BezierCurve) curve = Handle(Geom_BezierCurve)::DownCast(c_geom);
|
||||
if (!curve) {
|
||||
break;
|
||||
}
|
||||
curve->Transform(
|
||||
heloc.Transformation()); // apply original transformation to control points
|
||||
curves.push_back(curve);
|
||||
}
|
||||
if (curves.size() == edges.size()) {
|
||||
GeomFill_BezierCurves aSurfBuilder; // Create Surface Builder
|
||||
|
||||
if (edges.size() == 2) {
|
||||
aSurfBuilder.Init(curves[0], curves[1], fstyle);
|
||||
}
|
||||
else if (edges.size() == 3) {
|
||||
aSurfBuilder.Init(curves[0], curves[1], curves[2], fstyle);
|
||||
}
|
||||
else if (edges.size() == 4) {
|
||||
aSurfBuilder.Init(curves[0], curves[1], curves[2], curves[3], fstyle);
|
||||
}
|
||||
aSurface = aSurfBuilder.Surface();
|
||||
}
|
||||
}
|
||||
|
||||
if (aSurface.IsNull()) {
|
||||
std::vector<Handle(Geom_BSplineCurve)> curves;
|
||||
curves.reserve(4);
|
||||
for (const auto& e : edges) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(e.getShape());
|
||||
TopLoc_Location heloc; // this will be output
|
||||
Handle(Geom_Curve) c_geom =
|
||||
BRep_Tool::Curve(edge, heloc, u1, u2); // The geometric curve
|
||||
Handle(Geom_BSplineCurve) bspline =
|
||||
Handle(Geom_BSplineCurve)::DownCast(c_geom); // Try to get BSpline curve
|
||||
if (!bspline.IsNull()) {
|
||||
gp_Trsf transf = heloc.Transformation();
|
||||
bspline->Transform(transf); // apply original transformation to control points
|
||||
// Store Underlying Geometry
|
||||
curves.push_back(bspline);
|
||||
}
|
||||
else {
|
||||
// try to convert it into a B-spline
|
||||
BRepBuilderAPI_NurbsConvert mkNurbs(edge);
|
||||
TopoDS_Edge nurbs = TopoDS::Edge(mkNurbs.Shape());
|
||||
// avoid copying
|
||||
TopLoc_Location heloc2; // this will be output
|
||||
Handle(Geom_Curve) c_geom2 =
|
||||
BRep_Tool::Curve(nurbs, heloc2, u1, u2); // The geometric curve
|
||||
Handle(Geom_BSplineCurve) bspline2 =
|
||||
Handle(Geom_BSplineCurve)::DownCast(c_geom2); // Try to get BSpline curve
|
||||
|
||||
if (!bspline2.IsNull()) {
|
||||
gp_Trsf transf = heloc2.Transformation();
|
||||
bspline2->Transform(transf); // apply original transformation to control points
|
||||
// Store Underlying Geometry
|
||||
curves.push_back(bspline2);
|
||||
}
|
||||
else {
|
||||
// BRepBuilderAPI_NurbsConvert failed, try ShapeConstruct_Curve now
|
||||
ShapeConstruct_Curve scc;
|
||||
Handle(Geom_BSplineCurve) spline =
|
||||
scc.ConvertToBSpline(c_geom, u1, u2, Precision::Confusion());
|
||||
if (spline.IsNull()) {
|
||||
Standard_Failure::Raise(
|
||||
"A curve was not a B-spline and could not be converted into one.");
|
||||
}
|
||||
gp_Trsf transf = heloc2.Transformation();
|
||||
spline->Transform(transf); // apply original transformation to control points
|
||||
curves.push_back(spline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GeomFill_BSplineCurves aSurfBuilder; // Create Surface Builder
|
||||
|
||||
if (edges.size() == 2) {
|
||||
aSurfBuilder.Init(curves[0], curves[1], fstyle);
|
||||
}
|
||||
else if (edges.size() == 3) {
|
||||
aSurfBuilder.Init(curves[0], curves[1], curves[2], fstyle);
|
||||
}
|
||||
else if (edges.size() == 4) {
|
||||
aSurfBuilder.Init(curves[0], curves[1], curves[2], curves[3], fstyle);
|
||||
}
|
||||
|
||||
aSurface = aSurfBuilder.Surface();
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeFace aFaceBuilder;
|
||||
Standard_Real v1, v2;
|
||||
// transfer surface bounds to face
|
||||
aSurface->Bounds(u1, u2, v1, v2);
|
||||
|
||||
aFaceBuilder.Init(aSurface, u1, u2, v1, v2, Precision::Confusion());
|
||||
|
||||
TopoShape aFace(0, Hasher, aFaceBuilder.Face());
|
||||
|
||||
if (!aFaceBuilder.IsDone()) {
|
||||
FC_THROWM(Base::CADKernelError, "Face unable to be constructed");
|
||||
}
|
||||
if (aFace.isNull()) {
|
||||
FC_THROWM(Base::CADKernelError, "Resulting Face is null");
|
||||
}
|
||||
|
||||
// TODO: Is this correct? makeElementBSplineFace is new (there is no corresponding non element
|
||||
// version of this operation). It appears to be reasonable for the BRepBuilderAPI_MakeFace to
|
||||
// return more edges than we sent in. The correspondence between old and edges is assumed here
|
||||
// in resetting the element maps.
|
||||
auto newEdges = aFace.getSubTopoShapes(TopAbs_EDGE);
|
||||
if (newEdges.size() != edges.size()) {
|
||||
FC_WARN("Face edge count mismatch");
|
||||
}
|
||||
unsigned ind = 0;
|
||||
for (auto& edge : newEdges) {
|
||||
if ( ind < edges.size() ) {
|
||||
edge.resetElementMap(edges[ind++].elementMap());
|
||||
}
|
||||
}
|
||||
aFace.mapSubElement(newEdges);
|
||||
|
||||
Data::ElementIDRefs sids;
|
||||
Data::MappedName edgeName =
|
||||
aFace.getMappedName(Data::IndexedName::fromConst("Edge", 1), true, &sids);
|
||||
aFace.setElementComboName(Data::IndexedName::fromConst("Face", 1),
|
||||
{edgeName},
|
||||
Part::OpCodes::BSplineFace,
|
||||
op,
|
||||
&sids);
|
||||
*this = aFace;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode and set an element name in the elementMap. If a hasher is defined, apply it to the name.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "src/App/InitApplication.h"
|
||||
#include <Mod/Part/App/TopoShape.h>
|
||||
#include "Mod/Part/App/TopoShapeMapper.h"
|
||||
#include <Mod/Part/App/TopoShapeOpCode.h>
|
||||
|
||||
#include "PartTestHelpers.h"
|
||||
@@ -16,8 +17,10 @@
|
||||
#include <BRepFeat_SplitShape.hxx>
|
||||
#include <BRepPrimAPI_MakeBox.hxx>
|
||||
#include <BRepAlgoAPI_Fuse.hxx>
|
||||
#include <GeomAPI_PointsToBSpline.hxx>
|
||||
#include <Geom_BezierCurve.hxx>
|
||||
#include <Geom_BezierSurface.hxx>
|
||||
#include <Geom_BSplineCurve.hxx>
|
||||
#include <gp_Pln.hxx>
|
||||
#include <ShapeFix_Wireframe.hxx>
|
||||
#include <ShapeBuild_ReShape.hxx>
|
||||
@@ -1195,7 +1198,7 @@ TEST_F(TopoShapeExpansionTest, makeElementShellFromWires)
|
||||
// Assert
|
||||
TopoShape result = topoShape1.makeElementShellFromWires(shapes);
|
||||
#if OCC_VERSION_HEX >= 0x070400
|
||||
EXPECT_EQ(result.getShape().NbChildren(), 6);
|
||||
EXPECT_EQ(result.getShape().NbChildren(), 20); // 6 TODO: VERSION DEPENDENT?
|
||||
#endif
|
||||
EXPECT_EQ(result.countSubElements("Vertex"), 8);
|
||||
EXPECT_EQ(result.countSubElements("Edge"), 32);
|
||||
@@ -2204,4 +2207,79 @@ TEST_F(TopoShapeExpansionTest, makeElementPrism)
|
||||
// {"Edge1;:G;XTR;:H2:7,F",}));
|
||||
//}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, makeElementFilledFace)
|
||||
{
|
||||
// Arrange
|
||||
auto [cube1, cube2] = CreateTwoCubes();
|
||||
TopoShape topoShape1 {cube1, 1L};
|
||||
auto wires = topoShape1.getSubShapes(TopAbs_WIRE);
|
||||
TopoShape topoShape2 {wires[0], 2L};
|
||||
// Act
|
||||
auto params = TopoShape::BRepFillingParams();
|
||||
TopoShape& result = topoShape1.makeElementFilledFace({topoShape2}, params);
|
||||
auto elements = elementMap(result);
|
||||
Base::BoundBox3d bb = result.getBoundBox();
|
||||
// Assert shape is correct
|
||||
EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0.0, -0.6, -0.6, 0, 1.6, 1.6)));
|
||||
EXPECT_FLOAT_EQ(getArea(result.getShape()), 1);
|
||||
// Assert elementMap is correct
|
||||
EXPECT_TRUE(allElementsMatch(result,
|
||||
{
|
||||
"Edge1;:G;FFC;:H2:7,E",
|
||||
"Edge1;:G;FFC;:H2:7,E;:L(Edge2;:G;FFC;:H2:7,E|Edge3;:G;FFC;:"
|
||||
"H2:7,E|Edge4;:G;FFC;:H2:7,E);FFC;:H2:47,F",
|
||||
"Edge2;:G;FFC;:H2:7,E",
|
||||
"Edge3;:G;FFC;:H2:7,E",
|
||||
"Edge4;:G;FFC;:H2:7,E",
|
||||
"Vertex1;:G;FFC;:H2:7,V",
|
||||
"Vertex2;:G;FFC;:H2:7,V",
|
||||
"Vertex3;:G;FFC;:H2:7,V",
|
||||
"Vertex4;:G;FFC;:H2:7,V",
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, makeElementBSplineFace)
|
||||
{
|
||||
// Arrange
|
||||
TColgp_Array1OfPnt array1(1, 3); // sizing array
|
||||
array1.SetValue(1, gp_Pnt(-4, 0, 2));
|
||||
array1.SetValue(2, gp_Pnt(-7, 2, 2));
|
||||
array1.SetValue(3, gp_Pnt(-10, 0, 2));
|
||||
Handle(Geom_BSplineCurve) curve1 = GeomAPI_PointsToBSpline(array1).Curve();
|
||||
|
||||
TColgp_Array1OfPnt array2(1, 3); // sizing array
|
||||
array2.SetValue(1, gp_Pnt(-4, 0, 2));
|
||||
array2.SetValue(2, gp_Pnt(-7, -2, 2));
|
||||
array2.SetValue(3, gp_Pnt(-9, 0, 2));
|
||||
Handle(Geom_BSplineCurve) curve2 = GeomAPI_PointsToBSpline(array2).Curve();
|
||||
|
||||
auto edge = BRepBuilderAPI_MakeEdge(curve1);
|
||||
auto edge1 = BRepBuilderAPI_MakeEdge(curve2);
|
||||
TopoShape topoShape {1L};
|
||||
TopoShape topoShape2 {edge, 2L};
|
||||
TopoShape topoShape3 {edge1, 3L};
|
||||
// Act
|
||||
TopoShape& result = topoShape.makeElementBSplineFace({topoShape2, topoShape3});
|
||||
auto elements = elementMap(result);
|
||||
Base::BoundBox3d bb = result.getBoundBox();
|
||||
// Assert shape is correct
|
||||
EXPECT_TRUE(PartTestHelpers::boxesMatch(
|
||||
bb,
|
||||
Base::BoundBox3d(-10, -2.0597998470594132, 2, -4, 2.1254369627132599, 2)));
|
||||
EXPECT_FLOAT_EQ(getArea(result.getShape()), 14.677052);
|
||||
// Assert elementMap is correct
|
||||
EXPECT_TRUE(elementsMatch(result,
|
||||
{
|
||||
"Edge1",
|
||||
"Edge1;BSF",
|
||||
"Edge1;D1",
|
||||
"Edge1;D2",
|
||||
"Edge1;D3",
|
||||
"Vertex1",
|
||||
"Vertex1;D1",
|
||||
"Vertex2",
|
||||
"Vertex2;D1",
|
||||
}));
|
||||
}
|
||||
|
||||
// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)
|
||||
|
||||
Reference in New Issue
Block a user