mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-17 14:02:27 +00:00
Refactoring
This commit is contained in:
@@ -20,12 +20,15 @@
|
||||
#ifndef IFCSHAPELIST_H
|
||||
#define IFCSHAPELIST_H
|
||||
|
||||
#include "../ifcgeom_schema_agnostic/IfcGeomRenderStyles.h"
|
||||
|
||||
#include <gp_GTrsf.hxx>
|
||||
#include <TopoDS_Shape.hxx>
|
||||
|
||||
#include "../ifcgeom_schema_agnostic/IfcGeomRenderStyles.h"
|
||||
#include <vector>
|
||||
|
||||
namespace IfcGeom {
|
||||
|
||||
class IFC_GEOM_API IfcRepresentationShapeItem {
|
||||
private:
|
||||
int id;
|
||||
@@ -51,6 +54,11 @@ namespace IfcGeom {
|
||||
void setStyle(std::shared_ptr<const SurfaceStyle> newStyle) { style = newStyle; }
|
||||
int ItemId() const { return id; }
|
||||
};
|
||||
|
||||
typedef std::vector<IfcRepresentationShapeItem> IfcRepresentationShapeItems;
|
||||
|
||||
namespace util {
|
||||
bool flatten_shape_list(const IfcGeom::IfcRepresentationShapeItems& shapes, TopoDS_Shape& result, bool fuse, double tol);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,13 +1,47 @@
|
||||
#include "base_utils.h"
|
||||
|
||||
#include <TopTools_IndexedMapOfShape.hxx>
|
||||
#include "../ifcparse/IfcLogger.h"
|
||||
|
||||
#include <TopExp.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Vertex.hxx>
|
||||
|
||||
#include <gp_GTrsf.hxx>
|
||||
#include <gp_GTrsf2d.hxx>
|
||||
|
||||
#include <Geom_Plane.hxx>
|
||||
#include <Geom_OffsetSurface.hxx>
|
||||
|
||||
#include <ShapeAnalysis_Curve.hxx>
|
||||
#include <ShapeAnalysis_Surface.hxx>
|
||||
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRepBndLib.hxx>
|
||||
|
||||
#include <BRepBuilderAPI_Transform.hxx>
|
||||
#include <BRepBuilderAPI_GTransform.hxx>
|
||||
#include <BRepBuilderAPI_MakePolygon.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepPrimAPI_MakePrism.hxx>
|
||||
#include <BRepPrimAPI_MakeHalfSpace.hxx>
|
||||
#include <BRepOffsetAPI_Sewing.hxx>
|
||||
|
||||
#include <GeomAPI_IntSS.hxx>
|
||||
#include <GeomAPI_IntCS.hxx>
|
||||
|
||||
#include <BRepGProp.hxx>
|
||||
#include <BRepGProp_Face.hxx>
|
||||
#include <GProp_GProps.hxx>
|
||||
|
||||
#include <ShapeFix_Shell.hxx>
|
||||
#include <ShapeFix_Solid.hxx>
|
||||
#include <ShapeFix_Shape.hxx>
|
||||
#include <BRepCheck_Analyzer.hxx>
|
||||
#include <BRepClass3d_SolidClassifier.hxx>
|
||||
|
||||
#include <TopTools_IndexedMapOfShape.hxx>
|
||||
|
||||
// For axis placements detect equality early in order for the
|
||||
// relatively computionaly expensive gp_Trsf calculation to be skipped
|
||||
bool IfcGeom::util::axis_equal(const gp_Ax3 & a, const gp_Ax3 & b, double tolerance) {
|
||||
@@ -153,3 +187,555 @@ gp_Trsf IfcGeom::util::combine_offset_and_rotation(const gp_Vec & offset, const
|
||||
|
||||
return rotation_transform * offset_transform;
|
||||
}
|
||||
|
||||
|
||||
bool IfcGeom::util::project(const Handle_Geom_Surface& srf, const TopoDS_Shape& shp, double& u1, double& v1, double& u2, double& v2, double widen) {
|
||||
// @todo std::unique_ptr for C++11
|
||||
ShapeAnalysis_Surface* sas = 0;
|
||||
Handle(Geom_Plane) pln;
|
||||
|
||||
if (srf->DynamicType() == STANDARD_TYPE(Geom_Plane)) {
|
||||
// Optimize projection for specific cases
|
||||
pln = Handle(Geom_Plane)::DownCast(srf);
|
||||
} else if (srf->DynamicType() == STANDARD_TYPE(Geom_OffsetSurface) && Handle(Geom_OffsetSurface)::DownCast(srf)->BasisSurface()->DynamicType() == STANDARD_TYPE(Geom_Plane)) {
|
||||
// For an offset planar surface the projected UV coords are the same as the basis surface
|
||||
pln = Handle(Geom_Plane)::DownCast(Handle(Geom_OffsetSurface)::DownCast(srf)->BasisSurface());
|
||||
} else {
|
||||
sas = new ShapeAnalysis_Surface(srf);
|
||||
}
|
||||
|
||||
u1 = v1 = +std::numeric_limits<double>::infinity();
|
||||
u2 = v2 = -std::numeric_limits<double>::infinity();
|
||||
|
||||
gp_Pnt median;
|
||||
int vertex_count = 0;
|
||||
for (TopExp_Explorer exp(shp, TopAbs_VERTEX); exp.More(); exp.Next(), ++vertex_count) {
|
||||
gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(exp.Current()));
|
||||
median.ChangeCoord() += p.XYZ();
|
||||
|
||||
gp_Pnt2d uv;
|
||||
if (sas) {
|
||||
uv = sas->ValueOfUV(p, 1e-3);
|
||||
} else {
|
||||
gp_Vec d = p.XYZ() - pln->Position().Location().XYZ();
|
||||
uv.SetX(d.Dot(pln->Position().XDirection()));
|
||||
uv.SetY(d.Dot(pln->Position().YDirection()));
|
||||
}
|
||||
|
||||
if (uv.X() < u1) u1 = uv.X();
|
||||
if (uv.Y() < v1) v1 = uv.Y();
|
||||
if (uv.X() > u2) u2 = uv.X();
|
||||
if (uv.Y() > v2) v2 = uv.Y();
|
||||
}
|
||||
|
||||
if (vertex_count > 0) {
|
||||
|
||||
// Add a little bit of resolution so that the median is shifted towards the mass
|
||||
// of the curve. This helps to find the parameter ordering for conic surfaces.
|
||||
for (TopExp_Explorer exp(shp, TopAbs_EDGE); exp.More(); exp.Next(), ++vertex_count) {
|
||||
const TopoDS_Edge& e = TopoDS::Edge(exp.Current());
|
||||
|
||||
double a, b;
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(e, a, b);
|
||||
gp_Pnt p;
|
||||
crv->D0((a + b) / 2., p);
|
||||
|
||||
median.ChangeCoord() += p.XYZ();
|
||||
}
|
||||
|
||||
median.ChangeCoord().Divide(vertex_count);
|
||||
gp_Pnt2d uv;
|
||||
if (sas) {
|
||||
uv = sas->ValueOfUV(median, 1e-3);
|
||||
} else {
|
||||
gp_Vec d = median.XYZ() - pln->Position().Location().XYZ();
|
||||
uv.SetX(d.Dot(pln->Position().XDirection()));
|
||||
uv.SetY(d.Dot(pln->Position().YDirection()));
|
||||
}
|
||||
|
||||
if (uv.X() < u1 || uv.X() > u2) {
|
||||
std::swap(u1, u2);
|
||||
}
|
||||
|
||||
u1 -= widen;
|
||||
u2 += widen;
|
||||
v1 -= widen;
|
||||
v2 += widen;
|
||||
|
||||
}
|
||||
|
||||
delete sas;
|
||||
return vertex_count > 0;
|
||||
}
|
||||
|
||||
|
||||
TopoDS_Shape IfcGeom::util::apply_transformation(const TopoDS_Shape& s, const gp_Trsf& t) {
|
||||
if (t.Form() == gp_Identity) {
|
||||
return s;
|
||||
} else {
|
||||
/// @todo set to 1. and exactly 1. or use epsilon?
|
||||
if (t.ScaleFactor() != 1.) {
|
||||
return BRepBuilderAPI_Transform(s, t, true);
|
||||
} else {
|
||||
return s.Moved(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TopoDS_Shape IfcGeom::util::apply_transformation(const TopoDS_Shape& s, const gp_GTrsf& t) {
|
||||
if (t.Form() == gp_Other) {
|
||||
return BRepBuilderAPI_GTransform(s, t, true);
|
||||
} else {
|
||||
|
||||
return apply_transformation(s, t.Trsf());
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::util::fit_halfspace(const TopoDS_Shape& a, const TopoDS_Shape& b, TopoDS_Shape& box, double& height, double tol) {
|
||||
TopExp_Explorer exp(b, TopAbs_FACE);
|
||||
if (!exp.More()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TopoDS_Face face = TopoDS::Face(exp.Current());
|
||||
exp.Next();
|
||||
|
||||
if (exp.More()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Handle(Geom_Surface) surf = BRep_Tool::Surface(face);
|
||||
|
||||
// const gp_XYZ xyz = a.Location().Transformation().TranslationPart();
|
||||
// std::cout << "dz " << xyz.Z() << std::endl;
|
||||
|
||||
if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Bnd_Box bb;
|
||||
BRepBndLib::Add(a, bb);
|
||||
|
||||
if (bb.IsVoid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double xs[2], ys[2], zs[2];
|
||||
bb.Get(xs[0], ys[0], zs[0], xs[1], ys[1], zs[1]);
|
||||
|
||||
gp_Pln pln = Handle(Geom_Plane)::DownCast(surf)->Pln();
|
||||
|
||||
gp_Pnt P = pln.Position().Location();
|
||||
gp_Vec z = pln.Position().Direction();
|
||||
gp_Vec x = pln.Position().XDirection();
|
||||
gp_Vec y = pln.Position().YDirection();
|
||||
|
||||
if (face.Orientation() != TopAbs_REVERSED) {
|
||||
z.Reverse();
|
||||
}
|
||||
|
||||
double D, Umin, Umax, Vmin, Vmax;
|
||||
D = 0.;
|
||||
Umin = Vmin = +std::numeric_limits<double>::infinity();
|
||||
Umax = Vmax = -std::numeric_limits<double>::infinity();
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
for (int k = 0; k < 2; ++k) {
|
||||
gp_Pnt p(xs[i], ys[j], zs[k]);
|
||||
|
||||
gp_Vec d = p.XYZ() - P.XYZ();
|
||||
const double u = d.Dot(x);
|
||||
const double v = d.Dot(y);
|
||||
const double w = d.Dot(z);
|
||||
|
||||
if (w > D) {
|
||||
D = w;
|
||||
}
|
||||
if (u < Umin) {
|
||||
Umin = u;
|
||||
}
|
||||
if (u > Umax) {
|
||||
Umax = u;
|
||||
}
|
||||
if (v < Vmin) {
|
||||
Vmin = v;
|
||||
}
|
||||
if (v > Vmax) {
|
||||
Vmax = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const double eps = tol * 1000.;
|
||||
|
||||
BRepBuilderAPI_MakePolygon poly;
|
||||
poly.Add(P.XYZ() + x.XYZ() * (Umin - eps) + y.XYZ() * (Vmin - eps));
|
||||
poly.Add(P.XYZ() + x.XYZ() * (Umax + eps) + y.XYZ() * (Vmin - eps));
|
||||
poly.Add(P.XYZ() + x.XYZ() * (Umax + eps) + y.XYZ() * (Vmax + eps));
|
||||
poly.Add(P.XYZ() + x.XYZ() * (Umin - eps) + y.XYZ() * (Vmax + eps));
|
||||
poly.Close();
|
||||
|
||||
BRepBuilderAPI_MakeFace mf(surf, poly.Wire(), true);
|
||||
|
||||
gp_Vec vec = gp_Vec(z.XYZ() * (D + eps));
|
||||
|
||||
BRepPrimAPI_MakePrism mp(mf.Face(), vec);
|
||||
box = mp.Shape();
|
||||
|
||||
height = D;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const Handle_Geom_Curve IfcGeom::util::intersect(const Handle_Geom_Surface& a, const Handle_Geom_Surface& b) {
|
||||
GeomAPI_IntSS x(a, b, 1.e-7);
|
||||
if (x.IsDone() && x.NbLines() == 1) {
|
||||
return x.Line(1);
|
||||
} else {
|
||||
return Handle_Geom_Curve();
|
||||
}
|
||||
}
|
||||
|
||||
const Handle_Geom_Curve IfcGeom::util::intersect(const Handle_Geom_Surface& a, const TopoDS_Face& b) {
|
||||
return intersect(a, BRep_Tool::Surface(b));
|
||||
}
|
||||
|
||||
const Handle_Geom_Curve IfcGeom::util::intersect(const TopoDS_Face& a, const Handle_Geom_Surface& b) {
|
||||
return intersect(BRep_Tool::Surface(a), b);
|
||||
}
|
||||
|
||||
bool IfcGeom::util::intersect(const Handle_Geom_Curve& a, const Handle_Geom_Surface& b, gp_Pnt& p) {
|
||||
GeomAPI_IntCS x(a, b);
|
||||
if (x.IsDone() && x.NbPoints() == 1) {
|
||||
p = x.Point(1);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::util::intersect(const Handle_Geom_Curve& a, const TopoDS_Face& b, gp_Pnt &c) {
|
||||
return intersect(a, BRep_Tool::Surface(b), c);
|
||||
}
|
||||
|
||||
bool IfcGeom::util::intersect(const Handle_Geom_Curve& a, const TopoDS_Shape& b, std::vector<gp_Pnt>& out) {
|
||||
TopExp_Explorer exp(b, TopAbs_FACE);
|
||||
gp_Pnt p;
|
||||
for (; exp.More(); exp.Next()) {
|
||||
if (intersect(a, TopoDS::Face(exp.Current()), p)) {
|
||||
out.push_back(p);
|
||||
}
|
||||
}
|
||||
return !out.empty();
|
||||
}
|
||||
|
||||
bool IfcGeom::util::intersect(const Handle_Geom_Surface& a, const TopoDS_Shape& b, std::vector< std::pair<Handle_Geom_Surface, Handle_Geom_Curve> >& out) {
|
||||
TopExp_Explorer exp(b, TopAbs_FACE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
const TopoDS_Face& f = TopoDS::Face(exp.Current());
|
||||
const Handle_Geom_Surface& s = BRep_Tool::Surface(f);
|
||||
Handle_Geom_Curve crv = intersect(a, s);
|
||||
if (!crv.IsNull()) {
|
||||
out.push_back(std::make_pair(s, crv));
|
||||
}
|
||||
}
|
||||
return !out.empty();
|
||||
}
|
||||
|
||||
bool IfcGeom::util::closest(const gp_Pnt& a, const std::vector<gp_Pnt>& b, gp_Pnt& c) {
|
||||
double minimal_distance = std::numeric_limits<double>::infinity();
|
||||
for (std::vector<gp_Pnt>::const_iterator it = b.begin(); it != b.end(); ++it) {
|
||||
const double d = a.Distance(*it);
|
||||
if (d < minimal_distance) {
|
||||
minimal_distance = d;
|
||||
c = *it;
|
||||
}
|
||||
}
|
||||
return minimal_distance != std::numeric_limits<double>::infinity();
|
||||
}
|
||||
|
||||
bool IfcGeom::util::project(const Handle_Geom_Curve& crv, const gp_Pnt& pt, gp_Pnt& p, double& u, double& d) {
|
||||
ShapeAnalysis_Curve sac;
|
||||
sac.Project(crv, pt, 1e-3, p, u, false);
|
||||
d = pt.Distance(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
double IfcGeom::util::shape_volume(const TopoDS_Shape& s) {
|
||||
GProp_GProps prop;
|
||||
BRepGProp::VolumeProperties(s, prop);
|
||||
return prop.Mass();
|
||||
}
|
||||
|
||||
double IfcGeom::util::face_area(const TopoDS_Face& f) {
|
||||
GProp_GProps prop;
|
||||
BRepGProp::SurfaceProperties(f, prop);
|
||||
return prop.Mass();
|
||||
}
|
||||
|
||||
bool IfcGeom::util::is_convex(const TopoDS_Wire& wire, double tol) {
|
||||
for (TopExp_Explorer exp1(wire, TopAbs_VERTEX); exp1.More(); exp1.Next()) {
|
||||
TopoDS_Vertex V1 = TopoDS::Vertex(exp1.Current());
|
||||
gp_Pnt P1 = BRep_Tool::Pnt(V1);
|
||||
// Store the neighboring points
|
||||
std::vector<gp_Pnt> neighbors;
|
||||
for (TopExp_Explorer exp3(wire, TopAbs_EDGE); exp3.More(); exp3.Next()) {
|
||||
TopoDS_Edge edge = TopoDS::Edge(exp3.Current());
|
||||
std::vector<gp_Pnt> edge_points;
|
||||
for (TopExp_Explorer exp2(edge, TopAbs_VERTEX); exp2.More(); exp2.Next()) {
|
||||
TopoDS_Vertex V2 = TopoDS::Vertex(exp2.Current());
|
||||
gp_Pnt P2 = BRep_Tool::Pnt(V2);
|
||||
edge_points.push_back(P2);
|
||||
}
|
||||
if (edge_points.size() != 2) continue;
|
||||
if (edge_points[0].IsEqual(P1, tol)) neighbors.push_back(edge_points[1]);
|
||||
else if (edge_points[1].IsEqual(P1, tol)) neighbors.push_back(edge_points[0]);
|
||||
}
|
||||
// There should be two of these
|
||||
if (neighbors.size() != 2) return false;
|
||||
// Now find the non neighboring points
|
||||
std::vector<gp_Pnt> non_neighbors;
|
||||
for (TopExp_Explorer exp2(wire, TopAbs_VERTEX); exp2.More(); exp2.Next()) {
|
||||
TopoDS_Vertex V2 = TopoDS::Vertex(exp2.Current());
|
||||
gp_Pnt P2 = BRep_Tool::Pnt(V2);
|
||||
if (P1.IsEqual(P2, tol)) continue;
|
||||
bool found = false;
|
||||
for (std::vector<gp_Pnt>::const_iterator it = neighbors.begin(); it != neighbors.end(); ++it) {
|
||||
if ((*it).IsEqual(P2, tol)) { found = true; break; }
|
||||
}
|
||||
if (!found) non_neighbors.push_back(P2);
|
||||
}
|
||||
// Calculate the angle between the two edges of the vertex
|
||||
gp_Dir dir1(neighbors[0].XYZ() - P1.XYZ());
|
||||
gp_Dir dir2(neighbors[1].XYZ() - P1.XYZ());
|
||||
const double angle = acos(dir1.Dot(dir2)) + 0.0001;
|
||||
// Now for the non-neighbors see whether a greater angle can be found with one of the edges
|
||||
for (std::vector<gp_Pnt>::const_iterator it = non_neighbors.begin(); it != non_neighbors.end(); ++it) {
|
||||
gp_Dir dir3((*it).XYZ() - P1.XYZ());
|
||||
const double angle2 = acos(dir3.Dot(dir1));
|
||||
const double angle3 = acos(dir3.Dot(dir2));
|
||||
if (angle2 > angle || angle3 > angle) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TopoDS_Shape IfcGeom::util::halfspace_from_plane(const gp_Pln& pln, const gp_Pnt& cent) {
|
||||
TopoDS_Face face = BRepBuilderAPI_MakeFace(pln).Face();
|
||||
return BRepPrimAPI_MakeHalfSpace(face, cent).Solid();
|
||||
}
|
||||
|
||||
gp_Pln IfcGeom::util::plane_from_face(const TopoDS_Face& face) {
|
||||
BRepGProp_Face prop(face);
|
||||
Standard_Real u1, u2, v1, v2;
|
||||
prop.Bounds(u1, u2, v1, v2);
|
||||
Standard_Real u = (u1 + u2) / 2.0;
|
||||
Standard_Real v = (v1 + v2) / 2.0;
|
||||
gp_Pnt p;
|
||||
gp_Vec n;
|
||||
prop.Normal(u, v, p, n);
|
||||
return gp_Pln(p, n);
|
||||
}
|
||||
|
||||
gp_Pnt IfcGeom::util::point_above_plane(const gp_Pln& pln, bool agree) {
|
||||
if (agree) {
|
||||
return pln.Location().Translated(pln.Axis().Direction());
|
||||
} else {
|
||||
return pln.Location().Translated(-pln.Axis().Direction());
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::util::is_compound(const TopoDS_Shape& shape) {
|
||||
bool has_solids = TopExp_Explorer(shape, TopAbs_SOLID).More() != 0;
|
||||
bool has_shells = TopExp_Explorer(shape, TopAbs_SHELL).More() != 0;
|
||||
bool has_compounds = TopExp_Explorer(shape, TopAbs_COMPOUND).More() != 0;
|
||||
bool has_faces = TopExp_Explorer(shape, TopAbs_FACE).More() != 0;
|
||||
return has_compounds && has_faces && !has_solids && !has_shells;
|
||||
}
|
||||
|
||||
bool IfcGeom::util::shape_to_face_list(const TopoDS_Shape& s, TopTools_ListOfShape& li) {
|
||||
TopExp_Explorer exp(s, TopAbs_FACE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
TopoDS_Face face = TopoDS::Face(exp.Current());
|
||||
li.Append(face);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::util::create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& shape, double tol) {
|
||||
TopTools_ListOfShape face_list;
|
||||
shape_to_face_list(compound, face_list);
|
||||
if (face_list.Extent() == 0) {
|
||||
return false;
|
||||
}
|
||||
return create_solid_from_faces(face_list, shape, tol);
|
||||
}
|
||||
|
||||
bool IfcGeom::util::create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& shape, double tol, bool force_sewing) {
|
||||
bool valid_shell = false;
|
||||
|
||||
if (face_list.Extent() == 1) {
|
||||
shape = face_list.First();
|
||||
// A bit dubious what to return here.
|
||||
return true;
|
||||
} else if (face_list.Extent() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TopTools_ListIteratorOfListOfShape face_iterator;
|
||||
|
||||
bool has_shared_edges = false;
|
||||
TopTools_MapOfShape edge_set;
|
||||
|
||||
// In case there are wire interesections or failures in non-planar wire triangulations
|
||||
// the idea is to let occt do an exhaustive search of edge partners. But we have not
|
||||
// found a case where this actually improves boolean ops later on.
|
||||
// if (!faceset_helper_ || !faceset_helper_->non_manifold()) {
|
||||
|
||||
for (face_iterator.Initialize(face_list); !force_sewing && face_iterator.More(); face_iterator.Next()) {
|
||||
// As soon as is detected one of the edges is shared, the assumption is made no
|
||||
// additional sewing is necessary.
|
||||
if (!has_shared_edges) {
|
||||
TopExp_Explorer exp(face_iterator.Value(), TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
if (edge_set.Contains(exp.Current())) {
|
||||
has_shared_edges = true;
|
||||
break;
|
||||
}
|
||||
edge_set.Add(exp.Current());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BRepOffsetAPI_Sewing sewing_builder;
|
||||
sewing_builder.SetTolerance(tol);
|
||||
sewing_builder.SetMaxTolerance(tol);
|
||||
sewing_builder.SetMinTolerance(tol);
|
||||
|
||||
BRep_Builder builder;
|
||||
TopoDS_Shell shell;
|
||||
builder.MakeShell(shell);
|
||||
|
||||
for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) {
|
||||
if (has_shared_edges) {
|
||||
builder.Add(shell, face_iterator.Value());
|
||||
} else {
|
||||
sewing_builder.Add(face_iterator.Value());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (has_shared_edges) {
|
||||
ShapeFix_Shell fix;
|
||||
fix.FixFaceOrientation(shell);
|
||||
shape = fix.Shape();
|
||||
} else {
|
||||
sewing_builder.Perform();
|
||||
shape = sewing_builder.SewedShape();
|
||||
}
|
||||
|
||||
BRepCheck_Analyzer ana(shape);
|
||||
valid_shell = ana.IsValid();
|
||||
|
||||
if (!valid_shell) {
|
||||
ShapeFix_Shape sfs(shape);
|
||||
sfs.Perform();
|
||||
shape = sfs.Shape();
|
||||
|
||||
BRepCheck_Analyzer reana(shape);
|
||||
valid_shell = reana.IsValid();
|
||||
}
|
||||
|
||||
valid_shell &= util::count(shape, TopAbs_SHELL) > 0;
|
||||
} catch (const Standard_Failure& e) {
|
||||
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
||||
Logger::Error(e.GetMessageString());
|
||||
} else {
|
||||
Logger::Error("Unknown error sewing shell");
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Error("Unknown error sewing shell");
|
||||
}
|
||||
|
||||
if (valid_shell) {
|
||||
|
||||
TopoDS_Shape complete_shape;
|
||||
TopExp_Explorer exp(shape, TopAbs_SHELL);
|
||||
|
||||
for (; exp.More(); exp.Next()) {
|
||||
TopoDS_Shape result_shape = exp.Current();
|
||||
|
||||
try {
|
||||
ShapeFix_Solid solid;
|
||||
solid.SetMaxTolerance(tol);
|
||||
TopoDS_Solid solid_shape = solid.SolidFromShell(TopoDS::Shell(exp.Current()));
|
||||
// @todo: BRepClass3d_SolidClassifier::PerformInfinitePoint() is done by SolidFromShell
|
||||
// and this is done again, to be able to catch errors during this process.
|
||||
// This is double work that should be avoided.
|
||||
if (!solid_shape.IsNull()) {
|
||||
try {
|
||||
BRepClass3d_SolidClassifier classifier(solid_shape);
|
||||
result_shape = solid_shape;
|
||||
classifier.PerformInfinitePoint(tol);
|
||||
if (classifier.State() == TopAbs_IN) {
|
||||
shape.Reverse();
|
||||
}
|
||||
} catch (const Standard_Failure& e) {
|
||||
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
||||
Logger::Error(e.GetMessageString());
|
||||
} else {
|
||||
Logger::Error("Unknown error classifying solid");
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Error("Unknown error classifying solid");
|
||||
}
|
||||
}
|
||||
} catch (const Standard_Failure& e) {
|
||||
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
||||
Logger::Error(e.GetMessageString());
|
||||
} else {
|
||||
Logger::Error("Unknown error creating solid");
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Error("Unknown error creating solid");
|
||||
}
|
||||
|
||||
if (complete_shape.IsNull()) {
|
||||
complete_shape = result_shape;
|
||||
} else {
|
||||
BRep_Builder B;
|
||||
if (complete_shape.ShapeType() != TopAbs_COMPOUND) {
|
||||
TopoDS_Compound C;
|
||||
B.MakeCompound(C);
|
||||
B.Add(C, complete_shape);
|
||||
complete_shape = C;
|
||||
Logger::Warning("Multiple components in IfcConnectedFaceSet");
|
||||
}
|
||||
B.Add(complete_shape, result_shape);
|
||||
}
|
||||
}
|
||||
|
||||
TopExp_Explorer loose_faces(shape, TopAbs_FACE, TopAbs_SHELL);
|
||||
|
||||
for (; loose_faces.More(); loose_faces.Next()) {
|
||||
BRep_Builder B;
|
||||
if (complete_shape.ShapeType() != TopAbs_COMPOUND) {
|
||||
TopoDS_Compound C;
|
||||
B.MakeCompound(C);
|
||||
B.Add(C, complete_shape);
|
||||
complete_shape = C;
|
||||
Logger::Warning("Loose faces in IfcConnectedFaceSet");
|
||||
}
|
||||
B.Add(complete_shape, loose_faces.Current());
|
||||
}
|
||||
|
||||
shape = complete_shape;
|
||||
|
||||
} else {
|
||||
Logger::Error("Failed to sew faceset");
|
||||
}
|
||||
|
||||
return valid_shell;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
#ifndef BASE_UTILS_H
|
||||
#define BASE_UTILS_H
|
||||
|
||||
#include <TopoDS_Shape.hxx>
|
||||
#include <gp_Ax3.hxx>
|
||||
#include <gp_Pln.hxx>
|
||||
#include <gp_Pnt.hxx>
|
||||
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <TopoDS_Shape.hxx>
|
||||
|
||||
#include <Geom_Curve.hxx>
|
||||
#include <Geom_Surface.hxx>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace IfcGeom {
|
||||
namespace util {
|
||||
@@ -26,6 +37,34 @@ namespace IfcGeom {
|
||||
gp_Trsf combine_offset_and_rotation(const gp_Vec &offset, const gp_Quaternion& rotation);
|
||||
|
||||
bool is_nested_compound_of_solid(const TopoDS_Shape& s, int depth = 0);
|
||||
|
||||
bool create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& solid, double tol);
|
||||
bool shape_to_face_list(const TopoDS_Shape& s, TopTools_ListOfShape& li);
|
||||
bool create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& solid, double tol, bool force_sewing = false);
|
||||
bool is_compound(const TopoDS_Shape& shape);
|
||||
bool is_convex(const TopoDS_Wire& wire, double tol);
|
||||
TopoDS_Shape halfspace_from_plane(const gp_Pln& pln, const gp_Pnt& cent);
|
||||
gp_Pln plane_from_face(const TopoDS_Face& face);
|
||||
gp_Pnt point_above_plane(const gp_Pln& pln, bool agree = true);
|
||||
|
||||
bool fit_halfspace(const TopoDS_Shape& a, const TopoDS_Shape& b, TopoDS_Shape& box, double& height, double tol);
|
||||
const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const Handle_Geom_Surface&);
|
||||
const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const TopoDS_Face&);
|
||||
const Handle_Geom_Curve intersect(const TopoDS_Face&, const Handle_Geom_Surface&);
|
||||
bool intersect(const Handle_Geom_Curve&, const Handle_Geom_Surface&, gp_Pnt&);
|
||||
bool intersect(const Handle_Geom_Curve&, const TopoDS_Face&, gp_Pnt&);
|
||||
bool intersect(const Handle_Geom_Curve&, const TopoDS_Shape&, std::vector<gp_Pnt>&);
|
||||
bool intersect(const Handle_Geom_Surface&, const TopoDS_Shape&, std::vector< std::pair<Handle_Geom_Surface, Handle_Geom_Curve> >&);
|
||||
bool closest(const gp_Pnt&, const std::vector<gp_Pnt>&, gp_Pnt&);
|
||||
bool project(const Handle_Geom_Curve&, const gp_Pnt&, gp_Pnt& p, double& u, double& d);
|
||||
bool project(const Handle_Geom_Surface&, const TopoDS_Shape&, double& u1, double& v1, double& u2, double& v2, double widen = 0.1);
|
||||
|
||||
|
||||
double shape_volume(const TopoDS_Shape& s);
|
||||
double face_area(const TopoDS_Face& f);
|
||||
|
||||
TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_Trsf&);
|
||||
TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_GTrsf&);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1357,3 +1357,16 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To
|
||||
bs.Append(b);
|
||||
return boolean_operation(settings, a, bs, op, result, fuzziness);
|
||||
}
|
||||
|
||||
const TopoDS_Shape& IfcGeom::util::ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid, double tol) {
|
||||
const bool is_comp = is_compound(shape);
|
||||
if (!is_comp) {
|
||||
return solid = shape;
|
||||
}
|
||||
|
||||
if (!create_solid_from_compound(shape, solid, tol)) {
|
||||
return solid = shape;
|
||||
}
|
||||
|
||||
return solid;
|
||||
}
|
||||
@@ -93,6 +93,8 @@ namespace IfcGeom {
|
||||
bool boolean_operation(const boolean_settings& settings, const TopoDS_Shape&, const TopTools_ListOfShape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.);
|
||||
|
||||
bool boolean_operation(const boolean_settings& settings, const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.);
|
||||
|
||||
const TopoDS_Shape& ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid, double tol);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../ifcparse/IfcLogger.h"
|
||||
#include "../ifcgeom_schema_agnostic/Kernel.h"
|
||||
#include "../ifcgeom_schema_agnostic/base_utils.h"
|
||||
#include "../ifcgeom_schema_agnostic/boolean_utils.h"
|
||||
#include "../ifcgeom_schema_agnostic/IfcGeomTree.h"
|
||||
|
||||
#include <TopExp.hxx>
|
||||
@@ -21,6 +22,9 @@
|
||||
#include <ShapeExtend_WireData.hxx>
|
||||
#include <Standard_Version.hxx>
|
||||
#include <GeomAPI_ExtremaCurveCurve.hxx>
|
||||
#include <BRepOffsetAPI_Sewing.hxx>
|
||||
#include <ShapeFix_Solid.hxx>
|
||||
#include <ShapeFix_ShapeTolerance.hxx>
|
||||
|
||||
#include <boost/range/irange.hpp>
|
||||
#include <boost/range/algorithm_ext/push_back.hpp>
|
||||
@@ -350,7 +354,25 @@ namespace {
|
||||
};
|
||||
}
|
||||
|
||||
bool IfcGeom::util::wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires, double eps, double eps_real) {
|
||||
namespace {
|
||||
double get_wire_intersection_tolerance(const IfcGeom::util::wire_tolerance_settings& settings, const TopoDS_Wire& wire) {
|
||||
if (settings.use_wire_intersection_tolerance) {
|
||||
// This corresponds to faceset_helper::epsilon
|
||||
if (settings.vertex_clustering_epsilon > 0.) {
|
||||
return settings.vertex_clustering_epsilon / 3.;
|
||||
} else {
|
||||
return (std::min)(IfcGeom::util::min_edge_length(wire) / 2., settings.precision * 10.);
|
||||
}
|
||||
} else {
|
||||
return 0.;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::util::wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires, const wire_tolerance_settings& settings) {
|
||||
double eps = get_wire_intersection_tolerance(settings, wire);
|
||||
double eps_real = settings.precision;
|
||||
|
||||
if (!wire.Closed()) {
|
||||
wires.Append(wire);
|
||||
return false;
|
||||
@@ -512,7 +534,7 @@ bool IfcGeom::util::wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfS
|
||||
|
||||
// @todo this is a change in behaviour with eps precomputed from the kernel
|
||||
// instead of adaptively calculated for the successive iterations.
|
||||
wire_intersections(sfw.Wire(), wires, eps, eps_real);
|
||||
wire_intersections(sfw.Wire(), wires, settings);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -567,4 +589,360 @@ void IfcGeom::util::select_largest(const TopTools_ListOfShape& shapes, TopoDS_Sh
|
||||
largest = it.Value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool IfcGeom::util::wire_to_sequence_of_point(const TopoDS_Wire& w, TColgp_SequenceOfPnt& p) {
|
||||
TopExp_Explorer exp(w, TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
double a, b;
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b);
|
||||
if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
exp.ReInit();
|
||||
|
||||
int i = 0;
|
||||
for (; exp.More(); exp.Next(), ++i) {
|
||||
TopoDS_Vertex v1, v2;
|
||||
TopExp::Vertices(TopoDS::Edge(exp.Current()), v1, v2, true);
|
||||
if (exp.More()) {
|
||||
if (i == 0) {
|
||||
p.Append(BRep_Tool::Pnt(v1));
|
||||
}
|
||||
p.Append(BRep_Tool::Pnt(v2));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IfcGeom::util::sequence_of_point_to_wire(const TColgp_SequenceOfPnt& p, TopoDS_Wire& w, bool close) {
|
||||
BRepBuilderAPI_MakePolygon builder;
|
||||
for (int i = 1; i <= p.Length(); ++i) {
|
||||
builder.Add(p.Value(i));
|
||||
}
|
||||
if (close) {
|
||||
builder.Close();
|
||||
}
|
||||
w = builder.Wire();
|
||||
}
|
||||
|
||||
void IfcGeom::util::remove_collinear_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol) {
|
||||
const int start = closed ? 1 : 2;
|
||||
const int end = polygon.Length() - (closed ? 0 : 1);
|
||||
std::vector<bool> to_remove(polygon.Length(), false);
|
||||
for (int i = start; i <= end; ++i) {
|
||||
const gp_Pnt& a = polygon.Value(((i - 2 + polygon.Length()) % polygon.Length()) + 1);
|
||||
const gp_Pnt& b = polygon.Value(i);
|
||||
const gp_Pnt& c = polygon.Value((i % polygon.Length()) + 1);
|
||||
const gp_Vec d1 = c.XYZ() - a.XYZ();
|
||||
const gp_Vec d2 = b.XYZ() - a.XYZ();
|
||||
const double dt = d2.Dot(d1) / d1.Dot(d1);
|
||||
const gp_Vec d3 = d1.Scaled(dt);
|
||||
const gp_Pnt b2 = a.XYZ() + d3.XYZ();
|
||||
if (b.Distance(b2) < tol) {
|
||||
to_remove[i - 1] = true;
|
||||
}
|
||||
}
|
||||
for (int i = (int)to_remove.size() - 1; i >= 0; --i) {
|
||||
if (to_remove[i]) {
|
||||
polygon.Remove(i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IfcGeom::util::remove_duplicate_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol) {
|
||||
tol *= tol;
|
||||
|
||||
for (;;) {
|
||||
bool removed = false;
|
||||
int n = polygon.Length() - (closed ? 0 : 1);
|
||||
for (int i = 1; i <= n; ++i) {
|
||||
// wrap around to the first point in case of a closed loop
|
||||
int j = (i % polygon.Length()) + 1;
|
||||
double dist = polygon.Value(i).SquareDistance(polygon.Value(j));
|
||||
if (dist < tol) {
|
||||
// do not remove the first or last point to
|
||||
// maintain connectivity with other wires
|
||||
if ((closed && j == 1) || (!closed && j == n)) polygon.Remove(i);
|
||||
else polygon.Remove(j);
|
||||
removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!removed) break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
// Returns the vertex part of an TopoDS_Edge edge that is not TopoDS_Vertex vertex
|
||||
TopoDS_Vertex find_other(const TopoDS_Edge& edge, const TopoDS_Vertex& vertex) {
|
||||
TopExp_Explorer exp(edge, TopAbs_VERTEX);
|
||||
while (exp.More()) {
|
||||
if (!exp.Current().IsSame(vertex)) {
|
||||
return TopoDS::Vertex(exp.Current());
|
||||
}
|
||||
exp.Next();
|
||||
}
|
||||
return TopoDS_Vertex();
|
||||
}
|
||||
|
||||
TopoDS_Edge find_next(const TopTools_IndexedMapOfShape& edge_set, const TopTools_IndexedDataMapOfShapeListOfShape& vertex_to_edges, const TopoDS_Vertex& current, const TopoDS_Edge& previous_edge) {
|
||||
const TopTools_ListOfShape& edges = vertex_to_edges.FindFromKey(current);
|
||||
TopTools_ListIteratorOfListOfShape eit;
|
||||
for (eit.Initialize(edges); eit.More(); eit.Next()) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(eit.Value());
|
||||
if (edge.IsSame(previous_edge)) continue;
|
||||
if (edge_set.Contains(edge)) {
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
return TopoDS_Edge();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool IfcGeom::util::fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape, double tol) {
|
||||
BRepOffsetAPI_Sewing sew;
|
||||
sew.Add(shape);
|
||||
|
||||
TopTools_IndexedDataMapOfShapeListOfShape edge_to_faces;
|
||||
TopTools_IndexedDataMapOfShapeListOfShape vertex_to_edges;
|
||||
std::set<int> visited;
|
||||
TopTools_IndexedMapOfShape edge_set;
|
||||
|
||||
TopExp::MapShapesAndAncestors(shape, TopAbs_EDGE, TopAbs_FACE, edge_to_faces);
|
||||
|
||||
const int num_edges = edge_to_faces.Extent();
|
||||
for (int i = 1; i <= num_edges; ++i) {
|
||||
const TopTools_ListOfShape& faces = edge_to_faces.FindFromIndex(i);
|
||||
const int count = faces.Extent();
|
||||
// Find only the non-manifold edges: Edges that are only part of a
|
||||
// single face and therefore part of the wire(s) we want to fill.
|
||||
if (count == 1) {
|
||||
const TopoDS_Shape& edge = edge_to_faces.FindKey(i);
|
||||
TopExp::MapShapesAndAncestors(edge, TopAbs_VERTEX, TopAbs_EDGE, vertex_to_edges);
|
||||
edge_set.Add(edge);
|
||||
}
|
||||
}
|
||||
|
||||
const int num_verts = vertex_to_edges.Extent();
|
||||
TopoDS_Vertex first, current;
|
||||
TopoDS_Edge previous_edge;
|
||||
|
||||
// Now loop over all the vertices that are part of the wire(s) to be filled
|
||||
for (int i = 1; i <= num_verts; ++i) {
|
||||
first = current = TopoDS::Vertex(vertex_to_edges.FindKey(i));
|
||||
// We keep track of the vertices we already used
|
||||
if (visited.find(vertex_to_edges.FindIndex(current)) != visited.end()) {
|
||||
continue;
|
||||
}
|
||||
// Given these vertices, try to find closed loops and create new
|
||||
// wires out of them.
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
for (;;) {
|
||||
visited.insert(vertex_to_edges.FindIndex(current));
|
||||
// Find the edge that the current vertex is part of and points
|
||||
// away from the previous vertex (null for the first vertex).
|
||||
TopoDS_Edge edge = find_next(edge_set, vertex_to_edges, current, previous_edge);
|
||||
if (edge.IsNull()) {
|
||||
return false;
|
||||
}
|
||||
TopoDS_Vertex other = find_other(edge, current);
|
||||
if (other.IsNull()) {
|
||||
// Dealing with a conical edge probably, for some reason
|
||||
// this works better than adding the edge directly.
|
||||
double u1, u2;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u1, u2);
|
||||
w.Add(BRepBuilderAPI_MakeEdge(crv, u1, u2));
|
||||
break;
|
||||
} else {
|
||||
w.Add(edge);
|
||||
}
|
||||
// See if the starting point of this loop has been reached. Note that
|
||||
// additional wires after this one potentially will be created.
|
||||
if (other.IsSame(first)) {
|
||||
break;
|
||||
}
|
||||
previous_edge = edge;
|
||||
current = other;
|
||||
}
|
||||
sew.Add(BRepBuilderAPI_MakeFace(w));
|
||||
previous_edge.Nullify();
|
||||
}
|
||||
|
||||
sew.Perform();
|
||||
shape = sew.SewedShape();
|
||||
|
||||
try {
|
||||
ShapeFix_Solid solid;
|
||||
solid.LimitTolerance(tol);
|
||||
shape = solid.SolidFromShell(TopoDS::Shell(shape));
|
||||
} catch (const Standard_Failure& e) {
|
||||
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
||||
Logger::Error(e.GetMessageString());
|
||||
} else {
|
||||
Logger::Error("Unknown error creating solid");
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Error("Unknown error creating solid");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IfcGeom::util::convert_curve_to_wire(const Handle(Geom_Curve)& curve, TopoDS_Wire& wire) {
|
||||
try {
|
||||
wire = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(curve));
|
||||
return true;
|
||||
} catch (const Standard_Failure& e) {
|
||||
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
||||
Logger::Error(e.GetMessageString());
|
||||
} else {
|
||||
Logger::Error("Unknown error converting curve to wire");
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Error("Unknown error converting curve to wire");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void IfcGeom::util::assert_closed_wire(TopoDS_Wire& wire, double tol) {
|
||||
if (wire.Closed() == 0) {
|
||||
TopoDS_Vertex v0, v1;
|
||||
TopExp::Vertices(wire, v0, v1);
|
||||
|
||||
gp_Pnt p1 = BRep_Tool::Pnt(v0);
|
||||
gp_Pnt p2 = BRep_Tool::Pnt(v1);
|
||||
|
||||
if (p1.Distance(p2) > tol) {
|
||||
BRepBuilderAPI_MakeWire mw;
|
||||
mw.Add(wire);
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(v0, v1).Edge());
|
||||
wire = mw.Wire();
|
||||
}
|
||||
|
||||
Logger::Warning("Wire not closed");
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::util::convert_wire_to_face(const TopoDS_Wire& w, TopoDS_Face& face, const IfcGeom::util::wire_tolerance_settings& settings) {
|
||||
TopoDS_Wire wire = w;
|
||||
|
||||
TopTools_ListOfShape results;
|
||||
|
||||
if (settings.use_wire_intersection_check && util::wire_intersections(wire, results, settings)) {
|
||||
Logger::Warning("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected");
|
||||
util::select_largest(results, wire);
|
||||
}
|
||||
|
||||
bool is_2d = true;
|
||||
TopExp_Explorer exp(wire, TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
double a, b;
|
||||
// @todo this does not handle fillets
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b);
|
||||
if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) {
|
||||
is_2d = false;
|
||||
break;
|
||||
}
|
||||
Handle(Geom_Line) line = Handle(Geom_Line)::DownCast(crv);
|
||||
if (line->Lin().Direction().Z() > ALMOST_ZERO) {
|
||||
is_2d = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_2d) {
|
||||
// For 2d wires (e.g. profiles) a higher tolerance for plane fitting is never required.
|
||||
ShapeFix_ShapeTolerance FTol;
|
||||
FTol.SetTolerance(wire, settings.precision, TopAbs_WIRE);
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeFace mf(wire, false);
|
||||
BRepBuilderAPI_FaceError er = mf.Error();
|
||||
|
||||
if (er != BRepBuilderAPI_FaceDone) {
|
||||
Logger::Error("Failed to create face.");
|
||||
return false;
|
||||
}
|
||||
face = mf.Face();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::util::convert_wire_to_faces(const TopoDS_Wire& w, TopoDS_Compound& faces, const IfcGeom::util::wire_tolerance_settings& settings) {
|
||||
bool is_2d = true;
|
||||
TopExp_Explorer exp(w, TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
double a, b;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b);
|
||||
if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) {
|
||||
is_2d = false;
|
||||
break;
|
||||
}
|
||||
Handle(Geom_Line) line = Handle(Geom_Line)::DownCast(crv);
|
||||
if (line->Lin().Direction().Z() > ALMOST_ZERO) {
|
||||
is_2d = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TopTools_ListOfShape results;
|
||||
if (settings.use_wire_intersection_check && util::wire_intersections(w, results, settings)) {
|
||||
Logger::Warning("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected");
|
||||
} else {
|
||||
results.Clear();
|
||||
results.Append(w);
|
||||
}
|
||||
|
||||
TopoDS_Compound C;
|
||||
BRep_Builder B;
|
||||
B.MakeCompound(faces);
|
||||
|
||||
std::list<std::pair<double, TopoDS_Face>> face_list;
|
||||
double max_area = 0.;
|
||||
|
||||
TopTools_ListIteratorOfListOfShape it(results);
|
||||
for (; it.More(); it.Next()) {
|
||||
const TopoDS_Wire& wire = TopoDS::Wire(it.Value());
|
||||
if (!is_2d) {
|
||||
// For 2d wires (e.g. profiles) a higher tolerance for plane fitting is never required.
|
||||
ShapeFix_ShapeTolerance FTol;
|
||||
FTol.SetTolerance(wire, settings.precision, TopAbs_WIRE);
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeFace mf(wire, false);
|
||||
BRepBuilderAPI_FaceError er = mf.Error();
|
||||
|
||||
if (er != BRepBuilderAPI_FaceDone) {
|
||||
Logger::Error("Failed to create face.");
|
||||
continue;
|
||||
}
|
||||
|
||||
TopoDS_Face face = mf.Face();
|
||||
const double m = face_area(face);
|
||||
|
||||
face_list.push_back({ m, face });
|
||||
if (m > max_area) {
|
||||
max_area = m;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& p : face_list) {
|
||||
if (p.first >= max_area / 10.) {
|
||||
B.Add(faces, p.second);
|
||||
} else {
|
||||
Logger::Warning("Ignoring self-intersection loop with area " + boost::lexical_cast<std::string>(p.first));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,5 +1,16 @@
|
||||
#ifndef WIRE_UTILS_H
|
||||
#define WIRE_UTILS_H
|
||||
|
||||
#include <gp_Pln.hxx>
|
||||
|
||||
#include <Geom_Curve.hxx>
|
||||
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <TopoDS_Shape.hxx>
|
||||
#include <TopoDS_Compound.hxx>
|
||||
|
||||
#include <TColgp_SequenceOfPnt.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
|
||||
#include <vector>
|
||||
@@ -16,13 +27,34 @@ namespace IfcGeom {
|
||||
TRIANGULATE_WIRE_NON_MANIFOLD,
|
||||
};
|
||||
|
||||
struct wire_tolerance_settings {
|
||||
bool use_wire_intersection_check;
|
||||
bool use_wire_intersection_tolerance;
|
||||
double vertex_clustering_epsilon;
|
||||
double precision;
|
||||
};
|
||||
|
||||
/// Triangulate the set of wires. The firstmost wire is assumed to be the outer wire.
|
||||
triangulate_wire_result triangulate_wire(const std::vector<TopoDS_Wire>& wires, TopTools_ListOfShape& faces);
|
||||
|
||||
// eps: tolerance added to wire intersection checks, can be zero
|
||||
// eps_real: tolerance used to construct new edge geometry around intersection points, cannot be zero
|
||||
bool wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires, double eps, double eps_real);
|
||||
bool wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires, const wire_tolerance_settings& settings);
|
||||
|
||||
void select_largest(const TopTools_ListOfShape& shapes, TopoDS_Shape& largest);
|
||||
|
||||
bool convert_wire_to_face(const TopoDS_Wire& wire, TopoDS_Face& face, const IfcGeom::util::wire_tolerance_settings& settings);
|
||||
|
||||
bool convert_wire_to_faces(const TopoDS_Wire& wire, TopoDS_Compound& face, const IfcGeom::util::wire_tolerance_settings& settings);
|
||||
|
||||
void assert_closed_wire(TopoDS_Wire& wire, double tol);
|
||||
|
||||
bool fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape, double tol);
|
||||
void remove_duplicate_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol);
|
||||
void remove_collinear_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol);
|
||||
bool wire_to_sequence_of_point(const TopoDS_Wire&, TColgp_SequenceOfPnt&);
|
||||
void sequence_of_point_to_wire(const TColgp_SequenceOfPnt&, TopoDS_Wire&, bool closed);
|
||||
|
||||
bool convert_curve_to_wire(const Handle(Geom_Curve)& curve, TopoDS_Wire& wire);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user