Quantities in convert and geomserver. Faceset helper for creating edge pairs.

This commit is contained in:
Thomas Krijnen
2018-11-02 16:10:06 +01:00
parent ad41f04f11
commit 3ff619c7ed
11 changed files with 722 additions and 215 deletions
+80
View File
@@ -45,6 +45,8 @@ inline static bool ALMOST_THE_SAME(const T& a, const T& b, double tolerance=ALMO
#include <TColgp_SequenceOfPnt.hxx>
#include <TopTools_ListOfShape.hxx>
#include <BOPAlgo_Operation.hxx>
#include <BRep_Builder.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include "../ifcparse/macros.h"
#include "../ifcparse/IfcParse.h"
@@ -107,6 +109,82 @@ public:
class IFC_GEOM_API MAKE_TYPE_NAME(Kernel) : public IfcGeom::Kernel {
private:
/*
faceset_helper traverses the forward instance references of IfcConnectedFaceSet and then provides a mapping
M of (IfcCartesianPoint, IfcCartesianPoint) -> TopoDS_Edge, where M(a, b) is a partner of M(b, a), ie share
the same underlying edge but with orientation reversed. This then later speeds op the process of creating a
manifold Shell / Solid from this set of faces. Only IfcPolyLoop instances are used. Points within the tolerance
threshiold are merged, so consider points a, b, c, distance(a, b) < eps then M(a, b) = Null, M(a, b) = M(a, c).
*/
class faceset_helper {
private:
MAKE_TYPE_NAME(Kernel)* kernel_;
std::map<int, int> vertex_mapping_;
std::map<std::pair<int, int>, TopoDS_Edge> edges_;
template <typename Fn>
void loop_(IfcSchema::IfcCartesianPoint::list::ptr& ps, const Fn& callback) {
if (ps->size() < 3) {
return;
}
auto a = *(ps->end() - 1);
auto A = a->data().id();
for (auto& b : *ps) {
auto B = b->data().id();
auto C = vertex_mapping_[A], D = vertex_mapping_[B];
bool fwd = C < D;
if (!fwd) {
std::swap(C, D);
}
if (C != D) {
callback(C, D, fwd);
A = B;
}
}
}
public:
faceset_helper(MAKE_TYPE_NAME(Kernel)* kernel, const IfcSchema::IfcConnectedFaceSet* l);
~faceset_helper();
bool edge(const IfcSchema::IfcCartesianPoint* a, const IfcSchema::IfcCartesianPoint* b, TopoDS_Edge& e) {
int A = vertex_mapping_[a->data().id()];
int B = vertex_mapping_[b->data().id()];
if (A == B) {
return false;
}
return edge(A, B, e);
}
bool edge(int A, int B, TopoDS_Edge& e) {
e = edges_[{A, B}];
return true;
}
bool wire(const IfcSchema::IfcPolyLoop* loop, TopoDS_Wire& wire) {
BRep_Builder builder;
builder.MakeWire(wire);
bool valid;
auto ps = loop->Polygon();
loop_(ps, [this, &builder, &wire, &valid](int A, int B, bool fwd) {
TopoDS_Edge e;
if (edge(A, B, e)) {
if (!fwd) {
e.Reverse();
}
builder.Add(wire, e);
valid = true;
}
});
if (valid) {
wire.Closed(true);
}
return valid;
}
};
double deflection_tolerance;
double wire_creation_tolerance;
double point_equality_tolerance;
@@ -115,6 +193,7 @@ private:
double ifc_planeangle_unit;
double modelling_precision;
double dimensionality;
faceset_helper* faceset_helper_;
#ifndef NO_CACHE
MAKE_TYPE_NAME(Cache) cache;
@@ -139,6 +218,7 @@ public:
, modelling_precision(0.00001)
, dimensionality(1.)
, placement_rel_to(0)
, faceset_helper_(nullptr)
{}
MAKE_TYPE_NAME(Kernel)(const MAKE_TYPE_NAME(Kernel)& other) : IfcGeom::Kernel(0) {
+7
View File
@@ -171,6 +171,13 @@ namespace IfcGeom {
: Element<P, PP>(geometry->settings() ,id, parent_id, name, type, guid, context, trsf, product)
, _geometry(geometry)
{}
bool calculate_projected_surface_area(double& along_x, double& along_y, double& along_z) const {
const auto& trsf = this->transformation().data();
const gp_Mat& mat = trsf.HVectorialPart();
gp_Ax3 ax(trsf.TranslationPart(), mat.Column(3), mat.Column(1));
return geometry().calculate_projected_surface_area(ax, along_x, along_y, along_z);
}
private:
BRepElement(const BRepElement& other);
BRepElement& operator=(const BRepElement& other);
+37 -27
View File
@@ -178,35 +178,45 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
if (is_interior == !process_interior) continue;
TopoDS_Wire wire;
if (!convert_wire(loop, wire)) {
if (faceset_helper_ && loop->as<IfcSchema::IfcPolyLoop>()) {
faceset_helper_->wire(loop->as<IfcSchema::IfcPolyLoop>(), wire);
} else if (!convert_wire(loop, wire)) {
Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary loop", loop);
delete mf;
return false;
}
/*
The approach below does not result in a significant speed-up
if (loop->declaration().is(IfcSchema::IfcPolyLoop::Class()) && processed == 0 && face_surface.IsNull()) {
IfcSchema::IfcPolyLoop* polyloop = (IfcSchema::IfcPolyLoop*) loop;
IfcSchema::IfcCartesianPoint::list::ptr points = polyloop->Polygon();
if (points->size() == 3) {
// Help Open Cascade by finding the plane more efficiently
IfcSchema::IfcCartesianPoint::list::it point_iterator = points->begin();
gp_Pnt a, b, c;
convert(*point_iterator++, a);
convert(*point_iterator++, b);
convert(*point_iterator++, c);
const gp_XYZ ab = (b.XYZ() - a.XYZ());
const gp_XYZ ac = (c.XYZ() - a.XYZ());
const gp_Vec cross = ab.Crossed(ac);
if (cross.SquareMagnitude() > ALMOST_ZERO) {
const gp_Dir n = cross;
face_surface = new Geom_Plane(a, n);
// The approach below does not result in a significant speed-up
if (loop->as<IfcSchema::IfcPolyLoop>() && processed == 0 && face_surface.IsNull()) {
TopExp_Explorer exp(wire, TopAbs_EDGE);
int count = 0;
TopoDS_Edge edges[2];
for (; exp.More(); exp.Next(), count++) {
if (count < 2) {
edges[count] = TopoDS::Edge(exp.Current());
}
}
if (count == 3) {
// Help Open Cascade by finding the plane more efficiently
double _, __;
Handle(Geom_Line) c1 = Handle(Geom_Line)::DownCast(BRep_Tool::Curve(edges[0], _, __));
Handle(Geom_Line) c2 = Handle(Geom_Line)::DownCast(BRep_Tool::Curve(edges[1], _, __));
const gp_Vec ab = c1->Position().Direction();
const gp_Vec ac = c2->Position().Direction();
const gp_Vec cross = ab.Crossed(ac);
if (cross.SquareMagnitude() > ALMOST_ZERO) {
const gp_Dir n = cross;
face_surface = new Geom_Plane(c1->Position().Location(), n);
}
} else {
gp_Pln pln;
approximate_plane_through_wire(wire, pln);
face_surface = new Geom_Plane(pln);
}
}
*/
if (!same_sense) {
wire.Reverse();
@@ -297,16 +307,16 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
TopTools_ListOfShape face_list;
triangulate_wire(wire, face_list);
TopoDS_Compound compound;
BRep_Builder builder;
builder.MakeCompound(compound);
TopoDS_Compound triangulation_compound;
BRep_Builder triangulation_builder;
triangulation_builder.MakeCompound(triangulation_compound);
TopTools_ListIteratorOfListOfShape face_iterator;
for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) {
builder.Add(compound, face_iterator.Value());
triangulation_builder.Add(triangulation_compound, face_iterator.Value());
}
face = compound;
face = triangulation_compound;
return true;
}
+154 -103
View File
@@ -297,30 +297,6 @@ namespace {
return M;
}
bool is_manifold(const TopoDS_Shape& a) {
TopTools_IndexedDataMapOfShapeListOfShape map;
TopExp::MapShapesAndAncestors(a, TopAbs_EDGE, TopAbs_FACE, map);
for (int i = 1; i <= map.Extent(); ++i) {
if (map.FindFromIndex(i).Extent() != 2) {
return false;
}
}
return true;
}
bool is_manifold(const TopTools_ListOfShape& l) {
TopTools_ListOfShape r;
TopTools_ListIteratorOfListOfShape it(l);
for (; it.More(); it.Next()) {
if (!is_manifold(it.Value())) {
return false;
}
}
return true;
}
void bounding_box_overlap(double p, const TopoDS_Shape& a, const TopTools_ListOfShape& b, TopTools_ListOfShape& c) {
Bnd_Box A;
BRepBndLib::Add(a, A);
@@ -3081,8 +3057,6 @@ bool IfcGeom::Kernel::wire_intersections(const TopoDS_Wire& wire, TopTools_ListO
// Only check non-consecutive edges
if (i == n - 1 && j == 0) continue;
bool unbounded_intersects;
double u11, u12, u21, u22, U1, U2;
GeomAPI_ExtremaCurveCurve ecc(
BRep_Tool::Curve(wd->Edge(i + 1), u11, u12),
@@ -3090,87 +3064,89 @@ bool IfcGeom::Kernel::wire_intersections(const TopoDS_Wire& wire, TopTools_ListO
);
// @todo: extend this to work in case of multiple extrema and curved segments.
if ((unbounded_intersects = (ecc.NbExtrema() == 1 && ecc.Distance(1) < eps))) {
const bool unbounded_intersects = (ecc.NbExtrema() == 1 && ecc.Distance(1) < eps);
if (unbounded_intersects) {
ecc.Parameters(1, U1, U2);
}
if (u11 > u12) {
std::swap(u11, u12);
}
if (u21 > u22) {
std::swap(u21, u22);
}
/// @todo: tfk: probably need different thresholds on non-linear curves
u11 -= eps;
u12 += eps;
u21 -= eps;
u22 += eps;
// tfk: code below is for ShapeAnalysis_Wire::CheckIntersectingEdges()
// IntRes2d_SequenceOfIntersectionPoint points2d;
// TColgp_SequenceOfPnt points3d;
// TColStd_SequenceOfReal errors;
// if (saw.CheckIntersectingEdges(i + 1, j + 1, points2d, points3d, errors)) {
if (unbounded_intersects && u11 < U1 && U1 < u12 && u21 < U2 && U2 < u22) {
intersected = true;
// Explore a forward and backward cycle from the intersection point
for (int fb = 0; fb <= 1; ++fb) {
const bool forward = fb == 0;
BRepBuilderAPI_MakeWire mw;
bool first = true;
for (bounded_int k(j, n);;) {
bool intersecting = k == j || k == i;
if (intersecting) {
TopoDS_Edge e = wd->Edge(k + 1);
TopoDS_Vertex v1, v2;
TopExp::Vertices(e, v1, v2);
const TopoDS_Vertex* v = first == forward ? &v2 : &v1;
// gp_Pnt p2 = points3d.Value(1);
gp_Pnt p1 = BRep_Tool::Pnt(*v);
gp_Pnt pp1, pp2;
ecc.Points(1, pp1, pp2);
const gp_Pnt& p2 = k == i ? pp1 : pp2;
// Substitute with a new edge from/to the intersection point
if (p1.Distance(p2) > getValue(GV_PRECISION) * 2) {
double _, __;
Handle_Geom_Curve crv = BRep_Tool::Curve(e, _, __);
BRepBuilderAPI_MakeEdge me(crv, p1, p2);
TopoDS_Edge ed = me.Edge();
mw.Add(ed);
}
first = false;
} else {
// Re-use original edge
mw.Add(wd->Edge(k+1));
}
if (k == i) {
break;
}
if (forward) {
++k;
} else {
--k;
}
}
// Recursively process both cuts
wire_intersections(mw.Wire(), wires);
if (u11 > u12) {
std::swap(u11, u12);
}
if (u21 > u22) {
std::swap(u21, u22);
}
/// @todo: tfk: probably need different thresholds on non-linear curves
u11 -= eps;
u12 += eps;
u21 -= eps;
u22 += eps;
// tfk: code below is for ShapeAnalysis_Wire::CheckIntersectingEdges()
// IntRes2d_SequenceOfIntersectionPoint points2d;
// TColgp_SequenceOfPnt points3d;
// TColStd_SequenceOfReal errors;
// if (saw.CheckIntersectingEdges(i + 1, j + 1, points2d, points3d, errors)) {
if (u11 < U1 && U1 < u12 && u21 < U2 && U2 < u22) {
intersected = true;
// Explore a forward and backward cycle from the intersection point
for (int fb = 0; fb <= 1; ++fb) {
const bool forward = fb == 0;
BRepBuilderAPI_MakeWire mw;
bool first = true;
for (bounded_int k(j, n);;) {
bool intersecting = k == j || k == i;
if (intersecting) {
TopoDS_Edge e = wd->Edge(k + 1);
TopoDS_Vertex v1, v2;
TopExp::Vertices(e, v1, v2);
const TopoDS_Vertex* v = first == forward ? &v2 : &v1;
// gp_Pnt p2 = points3d.Value(1);
gp_Pnt p1 = BRep_Tool::Pnt(*v);
gp_Pnt pp1, pp2;
ecc.Points(1, pp1, pp2);
const gp_Pnt& p2 = k == i ? pp1 : pp2;
// Substitute with a new edge from/to the intersection point
if (p1.Distance(p2) > getValue(GV_PRECISION) * 2) {
double _, __;
Handle_Geom_Curve crv = BRep_Tool::Curve(e, _, __);
BRepBuilderAPI_MakeEdge me(crv, p1, p2);
TopoDS_Edge ed = me.Edge();
mw.Add(ed);
}
first = false;
} else {
// Re-use original edge
mw.Add(wd->Edge(k + 1));
}
if (k == i) {
break;
}
if (forward) {
++k;
} else {
--k;
}
}
// Recursively process both cuts
wire_intersections(mw.Wire(), wires);
}
return true;
}
return true;
}
}
}
@@ -3473,3 +3449,78 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopoDS_Shap
return boolean_operation(a, bs, op, result, fuzziness);
}
#endif
namespace {
void find_neighbours(IfcGeom::impl::tree<int>& tree, std::vector<gp_Pnt>& pnts, std::set<int>& visited, int p, double eps) {
visited.insert(p);
Bnd_Box b;
b.Set(pnts[p]);
b.Enlarge(eps);
std::vector<int> js = tree.select_box(b, false);
for (int j : js) {
if (visited.find(j) == visited.end()) {
find_neighbours(tree, pnts, visited, j, eps);
}
}
}
}
IfcGeom::Kernel::faceset_helper::~faceset_helper() {
kernel_->faceset_helper_ = nullptr;
}
IfcGeom::Kernel::faceset_helper::faceset_helper(Kernel* kernel, const IfcSchema::IfcConnectedFaceSet* l)
: kernel_(kernel)
{
kernel->faceset_helper_ = this;
IfcSchema::IfcCartesianPoint::list::ptr points = IfcParse::traverse((IfcUtil::IfcBaseClass*) l)->as<IfcSchema::IfcCartesianPoint>();
std::vector<gp_Pnt> pnts(std::distance(points->begin(), points->end()));
std::vector<TopoDS_Vertex> vertices(pnts.size());
BRep_Builder B;
const double eps = kernel->getValue(GV_PRECISION);
IfcGeom::impl::tree<int> tree;
{
int i = 0;
for (auto& pt : *points) {
if (kernel->convert(pt, pnts[i])) {
B.MakeVertex(vertices[i], pnts[i], Precision::Confusion());
tree.add(i, vertices[i]);
i++;
}
}
}
std::map<std::pair<int, int>, int> edge_use;
for (int i = 0; i < pnts.size(); ++i) {
std::set<int> vs;
find_neighbours(tree, pnts, vs, i, eps);
for (int v : vs) {
if (v <= i) {
auto pt = *(points->begin() + v);
vertex_mapping_.insert({pt->data().id(), i});
}
}
}
IfcSchema::IfcPolyLoop::list::ptr loops = IfcParse::traverse((IfcUtil::IfcBaseClass*)l)->as<IfcSchema::IfcPolyLoop>();
for (auto& loop : *loops) {
auto ps = loop->Polygon();
loop_(ps, [&edge_use](int C, int D, bool) {
edge_use[{C, D}] ++;
});
}
for (auto& p : edge_use) {
int a, b;
std::tie(a, b) = p.first;
edges_[p.first] = BRepBuilderAPI_MakeEdge(vertices[a], vertices[b]);
}
}
+136 -1
View File
@@ -22,6 +22,9 @@
#include <BRep_Builder.hxx>
#include <TopoDS_Compound.hxx>
#include <Geom_Plane.hxx>
#include <GProp_GProps.hxx>
#include <BRepGProp.hxx>
#include "../ifcgeom/IfcGeom.h"
@@ -97,4 +100,136 @@ TopoDS_Compound IfcGeom::Representation::BRep::as_compound() const {
builder.Add(compound, moved_shape);
}
return compound;
}
}
namespace {
void accumulate(const gp_Ax3& ax, const gp_Dir& normal, double area, double& along_x, double& along_y, double& along_z) {
along_x += area * ax.XDirection().Dot(normal);
along_y += area * ax.YDirection().Dot(normal);
along_z += area * ax.Direction().Dot(normal);
}
void surface_area_along_direction(double tol, const TopoDS_Shape& s, const gp_Ax3& ax, double& along_x, double& along_y, double& along_z) {
along_x = along_y = along_z = 0.;
bool meshed = false;
// todo check whether manifold and divide by 2
TopExp_Explorer exp(s, TopAbs_FACE);
for (; exp.More(); exp.Next()) {
const TopoDS_Face& face = TopoDS::Face(exp.Current());
Handle(Geom_Surface) surf = BRep_Tool::Surface(face);
Handle(Geom_Plane) plane = Handle(Geom_Plane)::DownCast(surf);
if (surf->DynamicType() == STANDARD_TYPE(Geom_Plane)) {
GProp_GProps prop_area;
BRepGProp::SurfaceProperties(face, prop_area);
const double area = prop_area.Mass();
accumulate(ax, plane->Position().Direction(), area, along_x, along_y, along_z);
} else {
if (!meshed) {
try {
BRepMesh_IncrementalMesh(s, tol);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Failed to triangulate shape");
return;
}
meshed = true;
}
TopLoc_Location loc;
Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(face, loc);
if (!tri.IsNull()) {
const TColgp_Array1OfPnt& nodes = tri->Nodes();
std::vector<gp_XYZ> coords;
coords.reserve(nodes.Length());
for (int i = 1; i <= nodes.Length(); ++i) {
coords.push_back(nodes(i).Transformed(loc).XYZ());
}
const Poly_Array1OfTriangle& triangles = tri->Triangles();
for (int i = 1; i <= triangles.Length(); ++i) {
int n1, n2, n3;
if (face.Orientation() == TopAbs_REVERSED) {
triangles(i).Get(n3, n2, n1);
} else {
triangles(i).Get(n1, n2, n3);
}
const gp_XYZ& pt1 = coords[n1 - 1];
const gp_XYZ& pt2 = coords[n2 - 1];
const gp_XYZ& pt3 = coords[n3 - 1];
const gp_Vec v1 = pt2 - pt1;
const gp_Vec v2 = pt3 - pt2;
const gp_Vec v3 = pt1 - pt3;
gp_Dir normal = gp_Dir(v1^v2);
double edge_lengths[3] = { v1.Magnitude(), v2.Magnitude(), v3.Magnitude() };
std::sort(&edge_lengths[0], &edge_lengths[2]);
const double& a = edge_lengths[0];
const double& b = edge_lengths[1];
const double& c = edge_lengths[2];
const double area = 0.25 * sqrt((a + (b + c))*(c - (a - b))*(c + (a - b))*(a + (b - c)));
accumulate(ax, normal, area, along_x, along_y, along_z);
}
}
}
}
}
}
bool IfcGeom::Representation::BRep::calculate_surface_area(double& area) const {
area = 0.;
for (IfcGeom::IfcRepresentationShapeItems::const_iterator it = begin(); it != end(); ++it) {
GProp_GProps prop;
BRepGProp::SurfaceProperties(it->Shape(), prop);
area += prop.Mass();
}
return true;
}
bool IfcGeom::Representation::BRep::calculate_volume(double& volume) const {
volume = 0.;
for (IfcGeom::IfcRepresentationShapeItems::const_iterator it = begin(); it != end(); ++it) {
if (Kernel::is_manifold(it->Shape())) {
GProp_GProps prop;
BRepGProp::VolumeProperties(it->Shape(), prop);
volume += prop.Mass();
} else {
return false;
}
}
return true;
}
bool IfcGeom::Representation::BRep::calculate_projected_surface_area(const gp_Ax3 & ax, double & along_x, double & along_y, double & along_z) const {
along_x = along_y = along_z = 0.;
for (IfcGeom::IfcRepresentationShapeItems::const_iterator it = begin(); it != end(); ++it) {
double x, y, z;
surface_area_along_direction(settings().deflection_tolerance(), it->Shape(), ax, x, y, z);
if (Kernel::is_manifold(it->Shape())) {
x /= 2.;
y /= 2.;
z /= 2.;
}
along_x += x;
along_y += y;
along_z += z;
}
return true;
}
+4
View File
@@ -76,6 +76,10 @@ namespace IfcGeom {
const IfcGeom::IfcRepresentationShapeItems& shapes() const { return shapes_; }
const std::string& id() const { return id_; }
TopoDS_Compound as_compound() const;
bool calculate_volume(double&) const;
bool calculate_surface_area(double&) const;
bool calculate_projected_surface_area(const gp_Ax3& ax, double& along_x, double& along_y, double& along_z) const;
};
class IFC_GEOM_API Serialization : public Representation {
+8
View File
@@ -103,6 +103,8 @@
#include "../ifcgeom/IfcGeom.h"
#include <memory>
#define Kernel MAKE_TYPE_NAME(Kernel)
bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolid* l, TopoDS_Shape& shape) {
@@ -592,6 +594,12 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Shape& shape) {
std::unique_ptr<faceset_helper> helper_scope;
if (getValue(GV_MAX_FACES_TO_SEW) != -1) {
helper_scope.reset(new faceset_helper(this, l));
}
IfcSchema::IfcFace::list::ptr faces = l->CfsFaces();
TopTools_ListOfShape face_list;