mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 06:58:56 +00:00
Triangulate non-planar wires when invoked with --sew-shells
This commit is contained in:
@@ -230,6 +230,7 @@ public:
|
|||||||
void sequence_of_point_to_wire(const TColgp_SequenceOfPnt&, TopoDS_Wire&, bool closed);
|
void sequence_of_point_to_wire(const TColgp_SequenceOfPnt&, TopoDS_Wire&, bool closed);
|
||||||
bool approximate_plane_through_wire(const TopoDS_Wire&, gp_Pln&);
|
bool approximate_plane_through_wire(const TopoDS_Wire&, gp_Pln&);
|
||||||
bool flatten_wire(TopoDS_Wire&);
|
bool flatten_wire(TopoDS_Wire&);
|
||||||
|
bool triangulate_wire(const TopoDS_Wire&, TopTools_ListOfShape&);
|
||||||
|
|
||||||
static double shape_volume(const TopoDS_Shape& s);
|
static double shape_volume(const TopoDS_Shape& s);
|
||||||
static double face_area(const TopoDS_Face& f);
|
static double face_area(const TopoDS_Face& f);
|
||||||
|
|||||||
@@ -271,6 +271,31 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
|
|||||||
} else {
|
} else {
|
||||||
const bool non_planar = mf->Error() == BRepBuilderAPI_NotPlanar;
|
const bool non_planar = mf->Error() == BRepBuilderAPI_NotPlanar;
|
||||||
delete mf;
|
delete mf;
|
||||||
|
|
||||||
|
const bool sewing_shells = getValue(GV_MAX_FACES_TO_SEW) > -1;
|
||||||
|
|
||||||
|
if (non_planar && sewing_shells && bounds->size() == 1 && face_surface.IsNull()) {
|
||||||
|
Logger::Message(Logger::LOG_ERROR, "Triangulating face boundary", bound->entity);
|
||||||
|
|
||||||
|
// When creating a solid, flatting the boundary only postpones the issue to
|
||||||
|
// creating a topological manifold out of the individual faces.
|
||||||
|
TopTools_ListOfShape face_list;
|
||||||
|
triangulate_wire(wire, face_list);
|
||||||
|
|
||||||
|
TopoDS_Compound compound;
|
||||||
|
BRep_Builder builder;
|
||||||
|
builder.MakeCompound(compound);
|
||||||
|
|
||||||
|
TopTools_ListIteratorOfListOfShape face_iterator;
|
||||||
|
for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) {
|
||||||
|
builder.Add(compound, face_iterator.Value());
|
||||||
|
}
|
||||||
|
|
||||||
|
face = compound;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!non_planar || flattened_wire || !flatten_wire(wire)) {
|
if (!non_planar || flattened_wire || !flatten_wire(wire)) {
|
||||||
Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary", bound->entity);
|
Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary", bound->entity);
|
||||||
return false;
|
return false;
|
||||||
@@ -338,9 +363,6 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
|
|||||||
face.Reverse();
|
face.Reverse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapeFix_ShapeTolerance FTol;
|
|
||||||
FTol.SetTolerance(face, getValue(GV_PRECISION), TopAbs_FACE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete mf;
|
delete mf;
|
||||||
|
|||||||
@@ -166,9 +166,9 @@ bool IfcGeom::Kernel::create_solid_from_faces(const TopTools_ListOfShape& face_l
|
|||||||
TopTools_ListIteratorOfListOfShape face_iterator;
|
TopTools_ListIteratorOfListOfShape face_iterator;
|
||||||
|
|
||||||
BRepOffsetAPI_Sewing builder;
|
BRepOffsetAPI_Sewing builder;
|
||||||
builder.SetTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE));
|
builder.SetTolerance(getValue(GV_PRECISION));
|
||||||
builder.SetMaxTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE));
|
builder.SetMaxTolerance(getValue(GV_PRECISION));
|
||||||
builder.SetMinTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE));
|
builder.SetMinTolerance(getValue(GV_PRECISION));
|
||||||
for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) {
|
for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) {
|
||||||
builder.Add(face_iterator.Value());
|
builder.Add(face_iterator.Value());
|
||||||
}
|
}
|
||||||
@@ -195,7 +195,7 @@ bool IfcGeom::Kernel::create_solid_from_faces(const TopTools_ListOfShape& face_l
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
ShapeFix_Solid solid;
|
ShapeFix_Solid solid;
|
||||||
solid.LimitTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE));
|
solid.SetMaxTolerance(getValue(GV_PRECISION));
|
||||||
TopoDS_Solid solid_shape = solid.SolidFromShell(TopoDS::Shell(exp.Current()));
|
TopoDS_Solid solid_shape = solid.SolidFromShell(TopoDS::Shell(exp.Current()));
|
||||||
if (!solid_shape.IsNull()) {
|
if (!solid_shape.IsNull()) {
|
||||||
try {
|
try {
|
||||||
@@ -2639,6 +2639,93 @@ bool IfcGeom::Kernel::flatten_wire(TopoDS_Wire& wire) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IfcGeom::Kernel::triangulate_wire(const TopoDS_Wire& wire, TopTools_ListOfShape& faces) {
|
||||||
|
// This is a bit of a precarious approach, but seems to work for the
|
||||||
|
// versions of OCCT tested for. OCCT has a Delaunay triangulation function
|
||||||
|
// BRepMesh_Delaun, but it is notoriously hard to interpret the results
|
||||||
|
// (due to the Bowyer-Watson super triangle perhaps?). Therefore
|
||||||
|
// alternatively we use the regular OCCT incremental mesher on a new face
|
||||||
|
// created from the UV coordinates of the original wire. Pray to our gods
|
||||||
|
// that the vertex coordinates are unaffected by the meshing algorithm and
|
||||||
|
// map them back to 3d coordinates when iterating over the mesh triangles.
|
||||||
|
|
||||||
|
typedef std::pair<double, double> uv_node;
|
||||||
|
|
||||||
|
gp_Pln pln;
|
||||||
|
if (!approximate_plane_through_wire(wire, pln)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gp_XYZ& udir = pln.Position().XDirection().XYZ();
|
||||||
|
const gp_XYZ& vdir = pln.Position().YDirection().XYZ();
|
||||||
|
const gp_XYZ& pnt = pln.Position().Location().XYZ();
|
||||||
|
|
||||||
|
BRepTools_WireExplorer exp(wire);
|
||||||
|
BRepBuilderAPI_MakePolygon mp;
|
||||||
|
std::map<uv_node, gp_Pnt> mapping;
|
||||||
|
|
||||||
|
// Add UV coordinates to a newly created polygon
|
||||||
|
for (; exp.More(); exp.Next()) {
|
||||||
|
gp_Pnt p = BRep_Tool::Pnt(exp.CurrentVertex());
|
||||||
|
double u = (p.XYZ() - pnt).Dot(udir);
|
||||||
|
double v = (p.XYZ() - pnt).Dot(vdir);
|
||||||
|
mp.Add(gp_Pnt(u, v, 0));
|
||||||
|
mapping.insert(std::make_pair(std::make_pair(u, v), p));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not closed by default
|
||||||
|
mp.Close();
|
||||||
|
|
||||||
|
// Create a new face from the {u,v,0} wire and mesh the face
|
||||||
|
TopoDS_Face face = BRepBuilderAPI_MakeFace(mp.Wire());
|
||||||
|
BRepMesh_IncrementalMesh(face, Precision::Confusion());
|
||||||
|
|
||||||
|
int n123[3];
|
||||||
|
TopLoc_Location loc;
|
||||||
|
Handle_Poly_Triangulation tri = BRep_Tool::Triangulation(face, loc);
|
||||||
|
|
||||||
|
if (!tri.IsNull()) {
|
||||||
|
const TColgp_Array1OfPnt& nodes = tri->Nodes();
|
||||||
|
|
||||||
|
const Poly_Array1OfTriangle& triangles = tri->Triangles();
|
||||||
|
for (int i = 1; i <= triangles.Length(); ++i) {
|
||||||
|
if (face.Orientation() == TopAbs_REVERSED)
|
||||||
|
triangles(i).Get(n123[2], n123[1], n123[0]);
|
||||||
|
else triangles(i).Get(n123[0], n123[1], n123[2]);
|
||||||
|
|
||||||
|
// Create polygons from the mesh vertices
|
||||||
|
BRepBuilderAPI_MakePolygon mp2;
|
||||||
|
for (int j = 0; j < 3; ++j) {
|
||||||
|
const gp_Pnt& uv = nodes.Value(n123[j]);
|
||||||
|
uv_node key = std::make_pair(uv.X(), uv.Y());
|
||||||
|
|
||||||
|
if (mapping.find(key) == mapping.end()) {
|
||||||
|
Logger::Error("Internal error: unable to unproject uv-mesh");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gp_Pnt& p = mapping.find(key)->second;
|
||||||
|
mp2.Add(p);
|
||||||
|
}
|
||||||
|
mp2.Close();
|
||||||
|
|
||||||
|
BRepBuilderAPI_MakeFace mf(mp2.Wire());
|
||||||
|
if (mf.IsDone()) {
|
||||||
|
TopoDS_Face triangle_face = mf.Face();
|
||||||
|
TopoDS_Iterator jt(triangle_face, false);
|
||||||
|
for (; jt.More(); jt.Next()) {
|
||||||
|
const TopoDS_Wire& w = TopoDS::Wire(jt.Value());
|
||||||
|
if (w.Orientation() != wire.Orientation()) {
|
||||||
|
triangle_face.Reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
faces.Append(triangle_face);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
TopoDS_Shape IfcGeom::Kernel::apply_transformation(const TopoDS_Shape& s, const gp_Trsf& t) {
|
TopoDS_Shape IfcGeom::Kernel::apply_transformation(const TopoDS_Shape& s, const gp_Trsf& t) {
|
||||||
if (t.Form() == gp_Identity) {
|
if (t.Form() == gp_Identity) {
|
||||||
|
|||||||
@@ -596,10 +596,25 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Sh
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (face_area(face) > getValue(GV_MINIMAL_FACE_AREA)) {
|
if (face.ShapeType() == TopAbs_COMPOUND) {
|
||||||
face_list.Append(face);
|
TopoDS_Iterator face_it(face, false);
|
||||||
|
for (; face_it.More(); face_it.Next()) {
|
||||||
|
if (face_it.Value().ShapeType() == TopAbs_FACE) {
|
||||||
|
// This should really be the case. This is not asserted.
|
||||||
|
const TopoDS_Face& triangle = TopoDS::Face(face_it.Value());
|
||||||
|
if (face_area(triangle) > getValue(GV_MINIMAL_FACE_AREA)) {
|
||||||
|
face_list.Append(triangle);
|
||||||
|
} else {
|
||||||
|
Logger::Message(Logger::LOG_WARNING, "Invalid face:", (*it)->entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Logger::Message(Logger::LOG_WARNING, "Invalid face:", (*it)->entity);
|
if (face_area(face) > getValue(GV_MINIMAL_FACE_AREA)) {
|
||||||
|
face_list.Append(face);
|
||||||
|
} else {
|
||||||
|
Logger::Message(Logger::LOG_WARNING, "Invalid face:", (*it)->entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user