You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For a sample problem consider 2 cases of operations performed:
A) Operations with offset:
Make test face,
Offset test face inward by offset, (using BRepFill_OffsetWire)
Make mask face,
Clip offset-face by mask face (using BRepAlgoAPI_Common)
Fillet vertices on the cut line. <--- (Fillet2d failed, status=6, ChFi2d_ComputationError)
B) Operation without offset:
Make test face,
skip this step, no offseting
Make mask face,
Clip test face by mask face (using BRepAlgoAPI_Common)
Fillet vertices on the cut line. <--- (Success)
Self-contained MWE to reproduce the issue:
#include<string>
#include<gtest/gtest.h>
#include<TopoDS.hxx>
#include<TopoDS_Face.hxx>
#include<TopoDS_Wire.hxx>
#include<TopExp_Explorer.hxx>
#include<gp_Pln.hxx>
#include<GeomAbs_JoinType.hxx>
#include<GeomAdaptor_Curve.hxx>
#include<GeomFill_Pipe.hxx>
#include<Geom_Circle.hxx>
#include<BRepTools.hxx>
#include<BRepAlgoAPI_Common.hxx>
#include<BRep_Builder.hxx>
#include<BRepBuilderAPI_MakeEdge.hxx>
#include<BRepBuilderAPI_MakeFace.hxx>
#include<BRepBuilderAPI_MakeWire.hxx>
#include<BRepCheck_Analyzer.hxx>
#include<BRepFill_OffsetWire.hxx>
#include<BRepFilletAPI_MakeFillet2d.hxx>
#include<BRepLib.hxx>namespace {
autodumpFaceVerts(const TopoDS_Face& f, constchar* label) {
std::cout << "\n--- " << label << " ---\n";
if (f.IsNull()) { std::cout << "Face is NULL\n"; return; }
const TopoDS_Wire outer = BRepTools::OuterWire(f);
int i = 0;
for (TopExp_Explorer ex(outer, TopAbs_VERTEX); ex.More(); ex.Next(), ++i) {
const TopoDS_Vertex v = TopoDS::Vertex(ex.Current());
const gp_Pnt p = BRep_Tool::Pnt(v);
std::cout << "v[" << i << "] = (" << p.X() << "," << p.Y() << "," << p.Z() << ")\n";
}
};
autopickClipCornerVertices(const TopoDS_Face& f, double xCut, double xTol) {
// Pick vertices on the clipped boundary x ~= xCut and return lowest/highest Y.
std::vector<TopoDS_Vertex> vs;
const TopoDS_Wire outer = BRepTools::OuterWire(f);
for (TopExp_Explorer ex(outer, TopAbs_VERTEX); ex.More(); ex.Next()) {
const TopoDS_Vertex v = TopoDS::Vertex(ex.Current());
const gp_Pnt p = BRep_Tool::Pnt(v);
if (std::abs(p.X() - xCut) <= xTol) vs.push_back(v);
}
std::ranges::sort(vs, [](const TopoDS_Vertex& a, const TopoDS_Vertex& b) {
returnBRep_Tool::Pnt(a).Y() < BRep_Tool::Pnt(b).Y();
});
// Dedup by Y (quick and dirty for an MWE)
std::vector<TopoDS_Vertex> uniq;
for (constauto& v : vs) {
constdouble y = BRep_Tool::Pnt(v).Y();
if (uniq.empty() || std::abs(y - BRep_Tool::Pnt(uniq.back()).Y()) > 1e-9) uniq.push_back(v);
}
return uniq;
};
const gp_Pln pln(gp_Pnt(0,0,0), gp_Dir(0,0,1));
TopoDS_Face make_test_face() {
// 1) Construct a section of a hollow 2D cylinder: annular sector on XY plane.constdouble rIn = 10.0;
constdouble rOut = 20.0;
constdouble ang0 = 0.0;
constdouble ang1 = 1.8;
const gp_Ax2 ax2(gp_Pnt(0,0,0), gp_Dir(0,0,1));
const gp_Circ cOut(ax2, rOut);
const gp_Circ cIn(ax2, rIn);
const gp_Pnt pOut0(rOut*std::cos(ang0), rOut*std::sin(ang0), 0);
const gp_Pnt pOut1(rOut*std::cos(ang1), rOut*std::sin(ang1), 0);
const gp_Pnt pIn0 (rIn *std::cos(ang0), rIn *std::sin(ang0), 0);
const gp_Pnt pIn1 (rIn *std::cos(ang1), rIn *std::sin(ang1), 0);
// Outer arc (ang0 -> ang1)
TopoDS_Edge eOut = BRepBuilderAPI_MakeEdge(cOut, ang0, ang1).Edge();
// Radial line at ang1 (outer -> inner)
TopoDS_Edge eRad1 = BRepBuilderAPI_MakeEdge(pOut1, pIn1).Edge();
// Inner arc (ang1 -> ang0) reversed direction to close sector
TopoDS_Edge eIn = BRepBuilderAPI_MakeEdge(cIn, ang0, ang1).Edge();
// Radial line at ang0 (inner -> outer)
TopoDS_Edge eRad0 = BRepBuilderAPI_MakeEdge(pIn0, pOut0).Edge();
BRepBuilderAPI_MakeWire mkW0;
mkW0.Add(eOut);
mkW0.Add(eRad1);
mkW0.Add(eIn);
mkW0.Add(eRad0);
const TopoDS_Wire w0 = mkW0.Wire();
BRepBuilderAPI_MakeFace mkF0(pln, w0, /*OnlyPlane=*/true);
return mkF0.Face();
}
TopoDS_Face offset_face(const TopoDS_Face &face, double off) {
std::cout << "offsetting face by " << off << "\n";
BRepFill_OffsetWire mkOffset(face, GeomAbs_Arc);
mkOffset.Perform(off);
const TopoDS_Shape offShape = mkOffset.Shape();
const TopoDS_Wire wOff = TopoDS::Wire(offShape);
std::cout << "offset wire valid? " << (BRepCheck_Analyzer(wOff).IsValid() ? "yes" : "no") << "\n";
// Build face from offset wire on the same plane
BRepBuilderAPI_MakeFace mkF1(pln, wOff, /*OnlyPlane=*/true);
mkF1.Build();
return mkF1.Face();
}
TopoDS_Face clip_with_mask(const TopoDS_Face& face, const TopoDS_Face& mask) {
BRepAlgoAPI_Common common(face, mask);
common.SetFuzzyValue(1e-7);
common.Build();
// Extract first face
TopoDS_Face clipped;
for (TopExp_Explorer ex(common.Shape(), TopAbs_FACE); ex.More(); ex.Next()) {
clipped = TopoDS::Face(ex.Current());
break;
}
return clipped;
}
voidexport_to_brep(const TopoDS_Shape& shape, const std::string& filename) {
std::ofstream file;
file.open(filename);
BRepTools::Write(shape, file, false, false, TopTools_FormatVersion_VERSION_2);
file.close();
}
}
TEST(Test2DFilletAfterOffset, with_offset) {
// 1. Make test face (section of 2D hollow cylider)const TopoDS_Face& face0 = make_test_face();
export_to_brep(face0, "test_with_offset_1_face0.brep");
dumpFaceVerts(face0, "BASE FACE");
// 2. Offset the face inward by 0.5auto face1 = offset_face(face0, 0.5);
export_to_brep(face0, "test_with_offset_2_face1.brep");
dumpFaceVerts(face1, "OFFSET FACE");
// 3) Make a mask for clippingconstdouble xCut = 0.0;
constdouble pad = 200.0; // big enough but not insane
BRepBuilderAPI_MakeFace mkMask(pln, -pad, xCut, -pad, pad);
const TopoDS_Face& mask = mkMask.Face();
export_to_brep(mask, "test_with_offset_3_mask.brep");
// 4) Clip the offseted face with the mask.auto clipped = clip_with_mask(face1, mask);
export_to_brep(clipped, "test_with_offset_4_clipped.brep");
dumpFaceVerts(clipped, "CLIPPED FACE");
// 4) Apply fillets to the edge of the clip (pick vertices on x=xCut)auto clip_verts = pickClipCornerVertices(clipped, xCut, 1e-7);
constexprdouble filletR = 0.1;
BRepFilletAPI_MakeFillet2d fillet(clipped);
fillet.AddFillet(clip_verts[0], filletR);
fillet.AddFillet(clip_verts[1], filletR);
fillet.Build();
// This assertion is intentionally loose: the MWE is to reproduce the failure.// If it succeeds on your machine, change parameters (ang1/off/pad) until it fails.EXPECT_TRUE(fillet.IsDone()) << "Fillet2d failed, status=" << (int)fillet.Status();
export_to_brep(fillet.Shape(), "test_with_offset_5_filleted.brep");
}
TEST(Test2DFilletAfterOffset, without_offset) {
// 1. Make test face (section of 2D hollow cylider)const TopoDS_Face& face0 = make_test_face();
export_to_brep(face0, "test_without_offset_1_face0.brep");
dumpFaceVerts(face0, "BASE FACE");
// 2. Skipping offset// Do nothing// 3. Make a mask for clippingconstdouble xCut = 0.0;
constdouble pad = 200.0; // big enough but not insane
BRepBuilderAPI_MakeFace mkMask(pln, -pad, xCut, -pad, pad);
const TopoDS_Face& mask = mkMask.Face();
export_to_brep(mask, "test_without_offset_3_mask.brep");
// 4. Clip the base face with the mask.auto clipped = clip_with_mask(face0, mask);
export_to_brep(clipped, "test_without_offset_4_clipped.brep");
dumpFaceVerts(clipped, "CLIPPED FACE");
// 5. Apply fillets to the edge of the clip (pick vertices on x=xCut)auto clip_verts = pickClipCornerVertices(clipped, xCut, 1e-7);
constexprdouble filletR = 0.1;
BRepFilletAPI_MakeFillet2d fillet(clipped);
fillet.AddFillet(clip_verts[0], filletR);
fillet.AddFillet(clip_verts[1], filletR);
fillet.Build();
// This assertion is intentionally loose: the MWE is to reproduce the failure.// If it succeeds on your machine, change parameters (ang1/off/pad) until it fails.EXPECT_TRUE(fillet.IsDone()) << "Fillet2d failed, status=" << (int)fillet.Status();
export_to_brep(fillet.Shape(), "test_without_offset_5_filleted.brep");
}
Notes:
I did try to debug up to a point but couldn't find the root cause. Obviously, the suspicion is that the offset operation breaks some toplogy and/or geometry consistency. Same behavior observed for other GeomAbs_JoinType values of BRepFill_OffsetWire. In some cases, when attempting to fillet a line-line corner on the offseted-and-clipped-face, the fillet is successful. Behavior is deterministic, not random. Debug output also provides:
122: ...
122: offsetting face by 0.5
122: BRepFill_TrimEdgeTool: incoherent intersection. Try with a greater tolerance
122: BRepFill_TrimEdgeTool: incoherent intersection. Try with a greater tolerance
122: BRepFill_TrimEdgeTool: incoherent intersection. Try with a greater tolerance
122: BRepFill_TrimEdgeTool: incoherent intersection. Try with a greater tolerance
122: BRepFill_TrimEdgeTool: incoherent intersection. Try with a greater tolerance
122: BRepFill_TrimEdgeTool: incoherent intersection. Try with a greater tolerance
122: BRepFill_TrimEdgeTool: incoherent intersection. Try with a greater tolerance
122: SeanceDeRattrapage = 7
122: BRepFill_TrimEdgeTool: incoherent intersection
122: ...
but BRepFill_OffsetWire does not provide tolerance control.
I would appreciate a quick sanity-check if perhaps I am doing something that's not intended by design on this operations. I'm sure I can work around this issue, but it seems more like a bug to me worth fixing.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi,
For a sample problem consider 2 cases of operations performed:
A) Operations with offset:
offset, (usingBRepFill_OffsetWire)BRepAlgoAPI_Common)B) Operation without offset:
BRepAlgoAPI_Common)Self-contained MWE to reproduce the issue:
Notes:
I did try to debug up to a point but couldn't find the root cause. Obviously, the suspicion is that the offset operation breaks some toplogy and/or geometry consistency. Same behavior observed for other
GeomAbs_JoinTypevalues ofBRepFill_OffsetWire. In some cases, when attempting to fillet aline-linecorner on theoffseted-and-clipped-face, the fillet is successful. Behavior is deterministic, not random. Debug output also provides:but
BRepFill_OffsetWiredoes not provide tolerance control.I would appreciate a quick sanity-check if perhaps I am doing something that's not intended by design on this operations. I'm sure I can work around this issue, but it seems more like a bug to me worth fixing.
Thank you!
~ Gordan
Beta Was this translation helpful? Give feedback.
All reactions