Proceed with merge

This commit is contained in:
Thomas Krijnen
2023-03-21 20:15:01 +01:00
parent cfb0eda067
commit c78b2893de
194 changed files with 105325 additions and 7785 deletions
@@ -1,14 +1,15 @@
#include "CgalConversionResult.h"
#include "CgalKernel.h"
#include "../../../ifcparse/IfcLogger.h"
#include "../../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h"
#include "../../../ifcgeom/IfcGeomRepresentation.h"
void ifcopenshell::geometry::CgalShape::Triangulate(const settings& settings, const ifcopenshell::geometry::taxonomy::matrix4& place, Representation::Triangulation* t, int surface_style_id) const {
void ifcopenshell::geometry::CgalShape::Triangulate(const IfcGeom::IteratorSettings& settings, const ifcopenshell::geometry::taxonomy::matrix4& place, IfcGeom::Representation::Triangulation* t, int surface_style_id) const {
// Copy is made because triangulate_faces() does not accept a const argument
cgal_shape_t s = shape_;
if (!place.components->isIdentity()) {
const auto& m = *place.components;
if (!place.is_identity()) {
const auto& m = place.ccomponents();
// @todo check
const cgal_placement_t trsf(
@@ -100,3 +101,29 @@ void ifcopenshell::geometry::CgalShape::Triangulate(const settings& settings, co
}
}
#include <CGAL/Polygon_mesh_processing/bbox.h>
double ifcopenshell::geometry::CgalShape::bounding_box(void *& b) const {
if (b == nullptr) {
b = new CGAL::Bbox_3;
}
auto& bb = (*((CGAL::Bbox_3*)b));
bb += CGAL::Polygon_mesh_processing::bbox(shape_);
return (bb.xmax() - bb.xmin()) * (bb.ymax() - bb.ymin()) * (bb.zmax() - bb.zmin());
}
int ifcopenshell::geometry::CgalShape::num_vertices() const {
return shape_.size_of_vertices();
}
void ifcopenshell::geometry::CgalShape::set_box(void * b) {
auto& bb = (*((CGAL::Bbox_3*)b));
Kernel_::Point_3 lower(bb.xmin(), bb.ymin(), bb.zmin());
Kernel_::Point_3 upper(bb.xmax(), bb.ymax(), bb.zmax());
shape_ = ifcopenshell::geometry::utils::create_cube(lower, upper);
}
int ifcopenshell::geometry::CgalShape::surface_genus() const {
throw std::runtime_error("Not implemented");
}
@@ -20,7 +20,9 @@
#ifndef CGALCONVERSIONRESULT_H
#define CGALCONVERSIONRESULT_H
#include "../../../ifcgeom/schema_agnostic/IfcGeomElement.h"
#include "../../../ifcgeom/IfcGeomElement.h"
#undef Handle
#include <boost/property_map/property_map.hpp>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
@@ -52,11 +54,11 @@ typedef CGAL::Polyhedron_3<Kernel_> cgal_shape_t;
typedef boost::graph_traits<CGAL::Polyhedron_3<Kernel_>>::vertex_descriptor cgal_vertex_descriptor_t;
typedef boost::graph_traits<CGAL::Polyhedron_3<Kernel_>>::face_descriptor cgal_face_descriptor_t;
#include "../../../ifcgeom/schema_agnostic/ConversionResult.h"
#include "../../../ifcgeom/ConversionResult.h"
namespace ifcopenshell { namespace geometry {
class CgalShape : public ConversionResultShape {
class CgalShape : public IfcGeom::ConversionResultShape {
public:
CgalShape(const cgal_shape_t& shape)
: shape_(shape)
@@ -65,13 +67,13 @@ namespace ifcopenshell { namespace geometry {
const cgal_shape_t& shape() const { return shape_; }
operator const cgal_shape_t& () { return shape_; }
virtual void Triangulate(const settings& settings, const ifcopenshell::geometry::taxonomy::matrix4& place, Representation::Triangulation* t, int surface_style_id) const;
virtual void Triangulate(const IfcGeom::IteratorSettings& settings, const ifcopenshell::geometry::taxonomy::matrix4& place, IfcGeom::Representation::Triangulation* t, int surface_style_id) const;
virtual void Serialize(std::string&) const {
throw std::runtime_error("Not implemented");
}
virtual ConversionResultShape* clone() const {
virtual IfcGeom::ConversionResultShape* clone() const {
return new CgalShape(shape_);
}
@@ -79,9 +81,14 @@ namespace ifcopenshell { namespace geometry {
throw std::runtime_error("Not implemented");
}
virtual int surface_genus() const {
throw std::runtime_error("Not implemented");
}
virtual double bounding_box(void*&) const;
virtual int num_vertices() const;
virtual void set_box(void*);
virtual int surface_genus() const;
private:
cgal_shape_t shape_;
};
File diff suppressed because it is too large Load Diff
+57 -85
View File
@@ -39,104 +39,76 @@ if ( it != cache.T.end() ) { e = it->second; return true; }
#include "../../../ifcparse/macros.h"
#include "../../../ifcgeom/kernel_agnostic/AbstractKernel.h"
#include "../../../ifcgeom/AbstractKernel.h"
#include "../../../ifcgeom/schema_agnostic/IfcGeomElement.h"
#include "../../../ifcgeom/IfcGeomElement.h"
#include "../../../ifcgeom/kernels/cgal/CgalConversionResult.h"
struct PolyhedronBuilder : public CGAL::Modifier_base<CGAL::Polyhedron_3<Kernel_>::HalfedgeDS> {
private:
std::list<cgal_face_t> *face_list;
std::list<cgal_face_t> *face_list;
public:
PolyhedronBuilder(std::list<cgal_face_t> *face_list) {
this->face_list = face_list;
}
void operator()(CGAL::Polyhedron_3<Kernel_>::HalfedgeDS &hds) {
std::list<Kernel_::Point_3> points;
std::list<std::list<std::size_t>> facet_vertices;
CGAL::Polyhedron_incremental_builder_3<CGAL::Polyhedron_3<Kernel_>::HalfedgeDS> builder(hds, true);
for (auto &face: *face_list) {
facet_vertices.push_back(std::list<std::size_t>());
for (auto &point: face.outer) {
facet_vertices.back().push_back(points.size());
points.push_back(point);
}
}
builder.begin_surface(points.size(), facet_vertices.size());
for (auto &point: points) {
// std::cout << "Adding point " << point << std::endl;
builder.add_vertex(point);
}
for (auto &facet: facet_vertices) {
builder.begin_facet();
// std::cout << "Adding facet ";
for (auto &vertex: facet) {
// std::cout << vertex << " ";
builder.add_vertex_to_facet(vertex);
}
// std::cout << std::endl;
builder.end_facet();
}
builder.end_surface();
}
boost::optional<cgal_shape_t> from_soup;
PolyhedronBuilder(std::list<cgal_face_t> *face_list);
void operator()(CGAL::Polyhedron_3<Kernel_>::HalfedgeDS &hds);
};
namespace ifcopenshell {
namespace geometry {
namespace utils {
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_cube(double d);
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_cube(const Kernel_::Point_3& lower, const Kernel_::Point_3& upper);
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_polyhedron(std::list<cgal_face_t> &face_list);
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_polyhedron(const CGAL::Nef_polyhedron_3<Kernel_> &nef_polyhedron);
IFC_GEOM_API CGAL::Nef_polyhedron_3<Kernel_> create_nef_polyhedron(std::list<cgal_face_t> &face_list);
IFC_GEOM_API CGAL::Nef_polyhedron_3<Kernel_> create_nef_polyhedron(CGAL::Polyhedron_3<Kernel_> &polyhedron);
}
namespace kernels {
class IFC_GEOM_API CgalKernel : public AbstractKernel {
private:
double precision_;
size_t circle_segments_;
CGAL::Nef_polyhedron_3<Kernel_> precision_cube_;
bool preprocess_boolean_operand(const IfcUtil::IfcBaseClass* log_reference, const cgal_shape_t& shape_const, CGAL::Nef_polyhedron_3<Kernel_>& result, bool dilate);
bool thin_solid(const CGAL::Nef_polyhedron_3<Kernel_>& a, CGAL::Nef_polyhedron_3<Kernel_>& result);
public:
CgalKernel()
: AbstractKernel("cgal")
// @todo
, precision_(1.e-5)
, circle_segments_(16)
{
auto cc = utils::create_cube(precision_);
precision_cube_ = CGAL::Nef_polyhedron_3<Kernel_>(cc);
namespace geometry {
namespace utils {
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_cube(double d);
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_cube(const Kernel_::Point_3& lower, const Kernel_::Point_3& upper);
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_polyhedron(std::list<cgal_face_t> &face_list, bool stitch_borders = false);
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_polyhedron(const CGAL::Nef_polyhedron_3<Kernel_> &nef_polyhedron);
IFC_GEOM_API CGAL::Nef_polyhedron_3<Kernel_> create_nef_polyhedron(std::list<cgal_face_t> &face_list);
IFC_GEOM_API CGAL::Nef_polyhedron_3<Kernel_> create_nef_polyhedron(CGAL::Polyhedron_3<Kernel_> &polyhedron);
}
void remove_duplicate_points_from_loop(cgal_wire_t& polygon);
namespace kernels {
bool convert(const taxonomy::extrusion*, cgal_shape_t&);
bool convert(const taxonomy::face*, cgal_face_t&);
bool convert(const taxonomy::loop*, cgal_wire_t&);
// bool convert(const taxonomy::matrix4*, cgal_placement_t&);
bool convert(const taxonomy::shell*, cgal_shape_t&);
// virtual bool convert_impl(const taxonomy::face*, ifcopenshell::geometry::ConversionResults&);
virtual bool convert_impl(const taxonomy::shell*, ifcopenshell::geometry::ConversionResults&);
virtual bool convert_impl(const taxonomy::extrusion*, ifcopenshell::geometry::ConversionResults&);
virtual bool convert_impl(const taxonomy::boolean_result*, ifcopenshell::geometry::ConversionResults&);
class IFC_GEOM_API CgalKernel : public AbstractKernel {
private:
size_t circle_segments_;
// CGAL::Nef_polyhedron_3<Kernel_> precision_cube_;
const CGAL::Nef_polyhedron_3<Kernel_>& precision_cube() const { return precision_cube_; }
};
bool preprocess_boolean_operand(const IfcUtil::IfcBaseClass* log_reference, const cgal_shape_t& shape_const, CGAL::Nef_polyhedron_3<Kernel_>& result, bool dilate);
bool thin_solid(const CGAL::Nef_polyhedron_3<Kernel_>& a, CGAL::Nef_polyhedron_3<Kernel_>& result);
}
}
CGAL::Nef_polyhedron_3<Kernel_> create_precision_cube_() const {
auto cc = utils::create_cube(conv_settings_.getValue(ConversionSettings::GV_PRECISION));
return CGAL::Nef_polyhedron_3<Kernel_>(cc);
}
public:
CgalKernel(const ConversionSettings& settings)
: AbstractKernel("cgal", settings)
, circle_segments_(32)
{}
void remove_duplicate_points_from_loop(cgal_wire_t& polygon);
bool convert(const taxonomy::extrusion*, cgal_shape_t&);
bool convert(const taxonomy::face*, cgal_face_t&);
bool convert(const taxonomy::loop*, cgal_wire_t&);
// bool convert(const taxonomy::matrix4*, cgal_placement_t&);
bool convert(const taxonomy::shell*, cgal_shape_t&);
bool process_extrusion(const cgal_face_t& bottom_face, const taxonomy::direction3& direction, double height, cgal_shape_t& shape);
bool process_as_2d_polygon(const taxonomy::boolean_result* br, std::list<CGAL::Polygon_2<Kernel_>>& loops, double& z0, double& z1);
bool process_as_2d_polygon(const std::list<std::list<std::pair<const IfcUtil::IfcBaseClass*, cgal_shape_t>>>& operands, std::list<CGAL::Polygon_2<Kernel_>>& loops, double& z0, double& z1);
virtual bool convert_impl(const taxonomy::shell*, IfcGeom::ConversionResults&);
virtual bool convert_impl(const taxonomy::extrusion*, IfcGeom::ConversionResults&);
virtual bool convert_impl(const taxonomy::boolean_result*, IfcGeom::ConversionResults&);
virtual bool convert_impl(const taxonomy::solid*, IfcGeom::ConversionResults&);
virtual bool convert_openings(const IfcUtil::IfcBaseEntity* entity, const std::vector<std::pair<taxonomy::item*, ifcopenshell::geometry::taxonomy::matrix4>>& openings,
const IfcGeom::ConversionResults& entity_shapes, const ifcopenshell::geometry::taxonomy::matrix4& entity_trsf, IfcGeom::ConversionResults& cut_shapes);
CGAL::Nef_polyhedron_3<Kernel_> precision_cube() const { return create_precision_cube_(); }
};
}
}
}
#endif
+36 -24
View File
@@ -20,13 +20,12 @@
#ifndef IFCGEOMTREE_H
#define IFCGEOMTREE_H
#include "../ifcparse/IfcFile.h"
#include "../../../ifcparse/IfcFile.h"
#include "../ifcgeom_schema_agnostic/IfcGeomElement.h"
#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h"
#include "../ifcgeom_schema_agnostic/IfcGeomMaterial.h"
#include "../ifcgeom_schema_agnostic/Kernel.h"
#include "../ifcgeom_schema_agnostic/base_utils.h"
#include "../../../ifcgeom/IfcGeomElement.h"
#include "../../../ifcgeom/Iterator.h"
#include "OpenCascadeConversionResult.h"
#include "base_utils.h"
#include <NCollection_UBTree.hxx>
#include <BRepBndLib.hxx>
@@ -39,13 +38,15 @@
#include <TopTools_DataMapOfShapeInteger.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepExtrema_ExtPF.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS.hxx>
namespace IfcGeom {
struct ray_intersection_result {
double distance;
int style_index;
IfcUtil::IfcBaseEntity* instance;
const IfcUtil::IfcBaseEntity* instance;
std::array<double, 3> position;
std::array<double, 3> normal;
double ray_distance;
@@ -267,8 +268,16 @@ namespace IfcGeom {
}
std::vector<T> select(const IfcGeom::BRepElement* elem, bool completely_within = false, double extend = -1.e-5) const {
auto compound = elem->geometry().as_compound();
compound.Move(elem->transformation().data());
auto shp = elem->geometry().as_compound();
auto compound = ((OpenCascadeShape*)shp)->shape();
const auto& m = elem->transformation().data().ccomponents();
gp_Trsf tr;
tr.SetValues(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
m(2, 0), m(2, 1), m(2, 2), m(2, 3)
);
compound.Move(tr);
return select(compound, completely_within, extend);
}
@@ -354,7 +363,7 @@ namespace IfcGeom {
};
}
class tree : public impl::tree<IfcUtil::IfcBaseEntity*> {
class tree : public impl::tree<const IfcUtil::IfcBaseEntity*> {
public:
tree() {};
@@ -377,7 +386,7 @@ namespace IfcGeom {
settings_.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true);
settings_.set(IfcGeom::IteratorSettings::SEW_SHELLS, true);
IfcGeom::Iterator it(settings_, &f);
IfcGeom::Iterator it(settings_, &f, {}, 1);
add_file(it);
}
@@ -394,27 +403,30 @@ namespace IfcGeom {
if (!elem) {
return;
}
auto compound = elem->geometry().as_compound();
compound.Move(elem->transformation().data());
auto compound_generic = elem->geometry().as_compound();
auto compound = ((ifcopenshell::geometry::OpenCascadeShape*)compound_generic)->shape();
const auto& m = elem->transformation().data().ccomponents();
gp_Trsf tr;
tr.SetValues(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
m(2, 0), m(2, 1), m(2, 2), m(2, 3)
);
compound.Move(tr);
add(elem->product(), compound);
auto git = elem->geometry().begin();
if (enable_face_styles_) {
TopoDS_Iterator it(compound);
for (; it.More(); it.Next(), ++git) {
std::unique_ptr<IfcGeom::Material> adaptor;
if (git->hasStyle()) {
adaptor.reset(new Material(git->StylePtr()));
} else {
adaptor.reset(new Material(IfcGeom::get_default_style(elem->type())));
}
// Assumption is that the number of styles is small, so the linear lookup time is not significant.
auto sit = std::find(styles_.begin(), styles_.end(), *adaptor);
auto sit = std::find(styles_.begin(), styles_.end(), git->Style());
size_t index;
if (sit == styles_.end()) {
index = styles_.size();
styles_.push_back(*adaptor);
styles_.push_back(git->Style());
} else {
index = std::distance(styles_.begin(), sit);
}
@@ -492,7 +504,7 @@ namespace IfcGeom {
enable_face_styles_ = b;
}
const std::vector<IfcGeom::Material>& styles() const {
const std::vector<ifcopenshell::geometry::taxonomy::style>& styles() const {
return styles_;
}
@@ -500,7 +512,7 @@ namespace IfcGeom {
typedef TopTools_DataMapOfShapeInteger face_style_map_t;
face_style_map_t face_styles_;
std::vector<IfcGeom::Material> styles_;
std::vector<ifcopenshell::geometry::taxonomy::style> styles_;
};
}
@@ -0,0 +1,228 @@
#include "OpenCascadeConversionResult.h"
#include "../../../ifcparse/IfcLogger.h"
#include "../../../ifcgeom/IfcGeomRepresentation.h"
#include <TopoDS.hxx>
#include <Geom_SphericalSurface.hxx>
#include <map>
namespace {
// We bypass the conversion to gp_GTrsf, because it does not work
void taxonomy_transform(const Eigen::Matrix4d* m, gp_XYZ& xyz) {
if (m) {
Eigen::Vector4d v(xyz.X(), xyz.Y(), xyz.Z(), 1.0);
auto v2 = (*m * v).eval();
xyz.ChangeData()[0] = v2(0);
xyz.ChangeData()[1] = v2(1);
xyz.ChangeData()[2] = v2(2);
}
}
}
void ifcopenshell::geometry::OpenCascadeShape::Triangulate(const IfcGeom::IteratorSettings& settings, const ifcopenshell::geometry::taxonomy::matrix4& place, IfcGeom::Representation::Triangulation* t, int surface_style_id) const {
// @todo remove duplication with OpenCascadeKernel::convert(const taxonomy::matrix4* matrix, gp_GTrsf& trsf);
// above can be static?
// A 3x3 matrix to rotate the vertex normals
boost::optional<gp_Mat> rotation_matrix;
if (place.components_) {
const auto& m = *place.components_;
rotation_matrix.emplace(
m(0, 0), m(0, 1), m(0, 2),
m(1, 0), m(1, 1), m(1, 2),
m(2, 0), m(2, 1), m(2, 2)
);
}
// Triangulate the shape
try {
BRepMesh_IncrementalMesh(shape_, settings.deflection_tolerance(), false, settings.angular_tolerance());
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Failed to triangulate shape");
return;
}
// Iterates over the faces of the shape
int num_faces = 0;
TopExp_Explorer exp;
for (exp.Init(shape_, TopAbs_FACE); exp.More(); exp.Next(), ++num_faces) {
TopoDS_Face face = TopoDS::Face(exp.Current());
TopLoc_Location loc;
Handle_Poly_Triangulation tri = BRep_Tool::Triangulation(face, loc);
if (tri.IsNull()) {
Logger::Message(Logger::LOG_ERROR, "Triangulation missing for face");
} else {
// Keep track of the number of times an edge is used
// Manifold edges (i.e. edges used twice) are deemed invisible
std::map<std::pair<int, int>, int> edgecount;
std::vector<std::pair<int, int> > edges_temp;
const TColgp_Array1OfPnt& nodes = tri->Nodes();
const TColgp_Array1OfPnt2d& uvs = tri->UVNodes();
std::vector<gp_XYZ> coords;
BRepGProp_Face prop(face);
std::map<int, int> dict;
// Vertex normals are only calculated if vertices are not welded and calculation is not disable explicitly.
const bool calculate_normals = !settings.get(IfcGeom::IteratorSettings::WELD_VERTICES) &&
!settings.get(IfcGeom::IteratorSettings::NO_NORMALS);
for (int i = 1; i <= nodes.Length(); ++i) {
coords.push_back(nodes(i).Transformed(loc).XYZ());
taxonomy_transform(place.components_, *coords.rbegin());
const gp_XYZ& last = *coords.rbegin();
dict[i] = t->addVertex(surface_style_id, last.X(), last.Y(), last.Z());
if (calculate_normals) {
const gp_Pnt2d& uv = uvs(i);
gp_Pnt p;
gp_Vec normal_direction;
prop.Normal(uv.X(), uv.Y(), p, normal_direction);
gp_Vec normal(0., 0., 0.);
if (normal_direction.Magnitude() > 1.e-9) {
if (rotation_matrix) {
normal = gp_Dir(normal_direction.XYZ() * *rotation_matrix);
} else {
normal = normal_direction;
}
} else {
Handle_Geom_Surface surf = BRep_Tool::Surface(face);
// Special case the normal at the poles of a spherical surface
if (surf->DynamicType() == STANDARD_TYPE(Geom_SphericalSurface)) {
if (fabs(fabs(uv.Y()) - M_PI / 2.) < 1.e-9) {
const bool is_top = uv.Y() > 0;
const bool is_forward = face.Orientation() == TopAbs_FORWARD;
const double z = (is_top == is_forward) ? 1. : -1.;
if (rotation_matrix) {
normal = gp_Dir(gp_XYZ(0, 0, z) * *rotation_matrix);
} else {
normal = gp_Dir(gp_XYZ(0, 0, z));
}
}
}
// TODO: Do the same for conical surfaces, but they are rare in IFC.
}
t->addNormal(normal.X(), normal.Y(), normal.Z());
}
}
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);
/* An alternative would be to calculate normals based
* on the coordinates of the mesh vertices */
/*
const gp_XYZ pt1 = coords[n1-1];
const gp_XYZ pt2 = coords[n2-1];
const gp_XYZ pt3 = coords[n3-1];
const gp_XYZ v1 = pt2-pt1;
const gp_XYZ v2 = pt3-pt2;
gp_Dir normal = gp_Dir(v1^v2);
_normals.push_back((float)normal.X());
_normals.push_back((float)normal.Y());
_normals.push_back((float)normal.Z());
*/
t->addFace(surface_style_id, dict[n1], dict[n2], dict[n3]);
t->addEdge(dict[n1], dict[n2], edgecount, edges_temp);
t->addEdge(dict[n2], dict[n3], edgecount, edges_temp);
t->addEdge(dict[n3], dict[n1], edgecount, edges_temp);
}
for (std::vector<std::pair<int, int> >::const_iterator jt = edges_temp.begin(); jt != edges_temp.end(); ++jt) {
if (edgecount[*jt] == 1) {
// non manifold edge, face boundary
t->registerEdge(jt->first, jt->second);
}
}
}
}
if (!t->normals().empty() && settings.get(IfcGeom::IteratorSettings::GENERATE_UVS)) {
t->uvs() = IfcGeom::Representation::Triangulation::box_project_uvs(t->verts(), t->normals());
}
if (num_faces == 0) {
// Edges are only emitted if there are no faces. A mixed representation of faces
// and loose edges is discouraged by the standard. An alternative would be to use
// TopExp_Explorer texp(s, TopAbs_EDGE, TopAbs_FACE) to find edges that do not
// belong to any face.
for (TopExp_Explorer texp(shape_, TopAbs_EDGE); texp.More(); texp.Next()) {
BRepAdaptor_Curve crv(TopoDS::Edge(texp.Current()));
GCPnts_QuasiUniformDeflection tessellater(crv, settings.deflection_tolerance());
int n = tessellater.NbPoints();
int previous = -1;
for (int i = 1; i <= n; ++i) {
gp_XYZ p = tessellater.Value(i).XYZ();
taxonomy_transform(place.components_, p);
int current = t->addVertex(surface_style_id, p.X(), p.Y(), p.Z());
std::vector<std::pair<int, int>> segments;
if (i > 1) {
segments.push_back(std::make_pair(previous, current));
}
if (settings.get(IfcGeom::IteratorSettings::EDGE_ARROWS)) {
// In case you want direction arrows on your edges
double u = tessellater.Parameter(i);
gp_XYZ p2, p3;
gp_Pnt tmp;
gp_Vec tmp2;
crv.D1(u, tmp, tmp2);
gp_Dir d1, d2, d3, d4;
d1 = tmp2;
if (texp.Current().Orientation() == TopAbs_REVERSED) {
d1 = -d1;
}
if (fabs(d1.Z()) < 0.5) {
d2 = d1.Crossed(gp::DZ());
} else {
d2 = d1.Crossed(gp::DY());
}
d3 = d1.XYZ() + d2.XYZ();
d4 = d1.XYZ() - d2.XYZ();
p2 = p - d3.XYZ() / 10.;
p3 = p - d4.XYZ() / 10.;
taxonomy_transform(place.components_, p2);
taxonomy_transform(place.components_, p3);
taxonomy_transform(place.components_, p);
int left = t->addVertex(surface_style_id, p2.X(), p2.Y(), p2.Z());
int right = t->addVertex(surface_style_id, p3.X(), p3.Y(), p3.Z());
segments.push_back(std::make_pair(left, current));
segments.push_back(std::make_pair(right, current));
}
for (auto& sgmt : segments) {
t->addEdge(surface_style_id, sgmt.first, sgmt.second);
}
previous = current;
}
}
}
BRepTools::Clean(shape_);
}
int ifcopenshell::geometry::OpenCascadeShape::surface_genus() const {
throw std::runtime_error("Not implemented");
}
bool ifcopenshell::geometry::OpenCascadeShape::is_manifold() const {
throw std::runtime_error("Not implemented");
}
@@ -0,0 +1,82 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef IFCGEOMOPENCASCADEREPRESENTATION_H
#define IFCGEOMOPENCASCADEREPRESENTATION_H
#include <BRepMesh_IncrementalMesh.hxx>
#include <BRepGProp_Face.hxx>
#include <Poly_Triangulation.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColgp_Array1OfPnt2d.hxx>
#include <TopExp_Explorer.hxx>
#include <BRepTools.hxx>
#include <gp_GTrsf.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <GCPnts_QuasiUniformDeflection.hxx>
#include "../../../ifcgeom/ConversionResult.h"
namespace ifcopenshell {
namespace geometry {
class OpenCascadeShape : public IfcGeom::ConversionResultShape {
public:
OpenCascadeShape(const TopoDS_Shape& shape)
: shape_(shape) {}
const TopoDS_Shape& shape() const { return shape_; }
operator const TopoDS_Shape& () { return shape_; }
virtual void Triangulate(const IfcGeom::IteratorSettings& settings, const ifcopenshell::geometry::taxonomy::matrix4& place, IfcGeom::Representation::Triangulation* t, int surface_style_id) const;
virtual void Serialize(std::string&) const {
throw std::runtime_error("Not implemented");
}
virtual IfcGeom::ConversionResultShape* clone() const {
return new OpenCascadeShape(shape_);
}
virtual bool is_manifold() const;
virtual double bounding_box(void*&) const {
throw std::runtime_error("Not implemented");
}
virtual int num_vertices() const {
throw std::runtime_error("Not implemented");
}
virtual void set_box(void*) {
throw std::runtime_error("Not implemented");
}
virtual int surface_genus() const;
private:
TopoDS_Shape shape_;
};
}
}
#endif
File diff suppressed because it is too large Load Diff
@@ -43,61 +43,25 @@
#include <BRep_Builder.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include "../ifcparse/macros.h"
#include "../ifcparse/IfcParse.h"
#include "../ifcparse/IfcBaseClass.h"
#include "../../../ifcgeom/AbstractKernel.h"
#include "../ifcgeom_schema_agnostic/IfcGeomElement.h"
#include "../ifcgeom_schema_agnostic/IfcGeomRepresentation.h"
#include "../ifcgeom_schema_agnostic/IfcRepresentationShapeItem.h"
#include "../ifcgeom_schema_agnostic/IfcGeomShapeType.h"
#include "../ifcgeom_schema_agnostic/Kernel.h"
#include "../ifcgeom_schema_agnostic/ifc_geom_api.h"
#include "../../../ifcgeom/IfcGeomElement.h"
#include "../../../ifcgeom/IfcGeomRepresentation.h"
#include "../../../ifcgeom/ConversionResult.h"
// Define this in case you want to conserve memory usage at all cost. This has been
// benchmarked extensively: https://github.com/IfcOpenShell/IfcOpenShell/pull/47
// #define NO_CACHE
#include "../../../ifcgeom/kernels/opencascade/OpenCascadeConversionResult.h"
#ifdef NO_CACHE
#include "../../../ifcgeom/ifc_geom_api.h"
#define IN_CACHE(T,E,t,e)
#define CACHE(T,E,e)
#include "../../../ifcgeom/taxonomy.h"
#include "../../../ifcgeom/ConversionSettings.h"
#else
#define IN_CACHE(T,E,t,e) std::map<int,t>::const_iterator it = cache.T.find(E->data().id());\
if ( it != cache.T.end() ) { e = it->second; return true; }
#define CACHE(T,E,e) cache.T[E->data().id()] = e;
#endif
#define INCLUDE_PARENT_DIR(x) STRINGIFY(../ifcparse/x.h)
#include INCLUDE_PARENT_DIR(IfcSchema)
#undef INCLUDE_PARENT_DIR
#define INCLUDE_PARENT_DIR(x) STRINGIFY(../ifcparse/x-definitions.h)
#include INCLUDE_PARENT_DIR(IfcSchema)
// @todo remove once merged to same ns.
using namespace ifcopenshell::geometry;
namespace IfcGeom {
class IFC_GEOM_API MAKE_TYPE_NAME(Cache) {
public:
#include "mapping_cache.i"
std::map<int, TopoDS_Shape> Shape;
};
namespace util {
template <typename T>
typename std::enable_if<std::is_pointer<T>::value, T&>::type conditional_address_of(T& t) {
return t;
}
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, T*>::type conditional_address_of(T& t) {
return &t;
}
}
class IFC_GEOM_API MAKE_TYPE_NAME(Kernel) : public IfcGeom::Kernel {
class IFC_GEOM_API OpenCascadeKernel : public kernels::AbstractKernel {
private:
/*
@@ -108,20 +72,18 @@ private:
threshiold are merged, so consider points a, b, c, distance(a, b) < eps then M(a, b) = Null, M(a, b) = M(a, c).
*/
template <typename CP=const IfcSchema::IfcCartesianPoint*, typename LP=const IfcSchema::IfcPolyLoop*>
class faceset_helper {
private:
MAKE_TYPE_NAME(Kernel)* kernel_;
std::set<typename std::conditional<std::is_pointer<LP>::value, LP, const LP*>::type> duplicates_;
std::map<const void*, int> vertex_mapping_;
OpenCascadeKernel* kernel_;
std::set<int> duplicates_;
std::map<int, int> vertex_mapping_;
std::map<std::pair<int, int>, TopoDS_Edge> edges_;
// not always in use
const std::vector<std::vector<double>>* points_ = nullptr;
double eps_;
bool non_manifold_;
void loop_(const LP& lp, const std::function<void(int, int, bool)>& callback);
void loop_(const taxonomy::loop* ps, const std::function<void(int, int, bool)>& callback);
/*
bool construct(const IfcSchema::IfcCartesianPoint* cp, gp_Pnt* l);
bool construct(const std::vector<double>& cp, gp_Pnt* l);
@@ -135,13 +97,9 @@ private:
std::vector<const void*> get_idxs(const IfcSchema::IfcPolyLoop* lp);
std::vector<const void*> get_idxs(const std::vector<int>& it);
*/
public:
faceset_helper(
MAKE_TYPE_NAME(Kernel)* kernel,
const std::vector<CP>& points,
const std::vector<LP>& indices,
bool should_by_closed);
faceset_helper(OpenCascadeKernel* kernel, const taxonomy::shell* l);
~faceset_helper();
bool non_manifold() const { return non_manifold_; }
@@ -150,280 +108,64 @@ private:
bool edge(int A, int B, TopoDS_Edge& e);
bool wire(const LP& loop, TopoDS_Wire& wire);
bool wires(const LP& loop, TopTools_ListOfShape& wires);
bool wire(const taxonomy::loop* loop, TopoDS_Wire& wire);
bool wires(const taxonomy::loop* loop, TopTools_ListOfShape& wires);
};
double deflection_tolerance;
double max_faces_to_orient;
double ifc_length_unit;
double ifc_planeangle_unit;
double modelling_precision;
double dimensionality;
double layerset_first;
double no_wire_intersection_check;
double no_wire_intersection_tolerance;
double precision_factor;
double boolean_debug_setting;
double boolean_attempt_2d;
// For stopping PlacementRelTo recursion in convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& trsf)
const IfcParse::declaration* placement_rel_to_type_;
const IfcUtil::IfcBaseEntity* placement_rel_to_instance_;
faceset_helper<>* faceset_helper_;
double disable_boolean_result;
faceset_helper* faceset_helper_;
// @todo these should be moved to the mapping
/*
gp_Vec offset = gp_Vec{0.0, 0.0, 0.0};
gp_Quaternion rotation = gp_Quaternion{};
gp_Trsf offset_and_rotation = gp_Trsf();
*/
#ifndef NO_CACHE
MAKE_TYPE_NAME(Cache) cache;
#endif
std::map<int, std::shared_ptr<const SurfaceStyle>> style_cache;
std::shared_ptr<const SurfaceStyle> internalize_surface_style(const std::pair<IfcUtil::IfcBaseClass*, IfcUtil::IfcBaseClass*>& shading_style);
double precision_;
public:
MAKE_TYPE_NAME(Kernel)()
: IfcGeom::Kernel()
, deflection_tolerance(0.001)
, max_faces_to_orient(-1.0)
, ifc_length_unit(1.0)
, ifc_planeangle_unit(-1.0)
, modelling_precision(0.00001)
, dimensionality(1.)
, layerset_first(-1.)
, no_wire_intersection_check(-1)
, no_wire_intersection_tolerance(-1)
, precision_factor(10.)
, boolean_debug_setting(false)
, boolean_attempt_2d(true)
, placement_rel_to_type_(nullptr)
, placement_rel_to_instance_(nullptr)
OpenCascadeKernel(const ConversionSettings& settings)
: AbstractKernel("opencascade", settings)
, faceset_helper_(nullptr)
, disable_boolean_result(-1.)
, precision_(settings.getValue(ConversionSettings::GV_PRECISION))
{}
MAKE_TYPE_NAME(Kernel)(const MAKE_TYPE_NAME(Kernel)& other)
: IfcGeom::Kernel()
, deflection_tolerance(other.deflection_tolerance)
, max_faces_to_orient(other.max_faces_to_orient)
, ifc_length_unit(other.ifc_length_unit)
, ifc_planeangle_unit(other.ifc_planeangle_unit)
, modelling_precision(other.modelling_precision)
, dimensionality(other.dimensionality)
, layerset_first(other.layerset_first)
, no_wire_intersection_check(other.no_wire_intersection_check)
, no_wire_intersection_tolerance(other.no_wire_intersection_tolerance)
, precision_factor(other.precision_factor)
, boolean_debug_setting(other.boolean_debug_setting)
, boolean_attempt_2d(other.boolean_attempt_2d)
, placement_rel_to_type_(other.placement_rel_to_type_)
, placement_rel_to_instance_(other.placement_rel_to_instance_)
// @nb faceset_helper_ always initialized to 0
, faceset_helper_(nullptr)
, disable_boolean_result(other.disable_boolean_result)
, offset(other.offset)
, rotation(other.rotation)
, offset_and_rotation(other.offset_and_rotation)
{
bool convert(const taxonomy::extrusion*, TopoDS_Shape&);
bool convert(const taxonomy::face*, TopoDS_Shape&);
bool convert(const taxonomy::loop*, TopoDS_Wire&);
bool convert(const taxonomy::matrix4*, gp_GTrsf&);
bool convert(const taxonomy::shell*, TopoDS_Shape&);
bool convert(const taxonomy::solid*, TopoDS_Shape&);
bool convert(const taxonomy::bspline_surface* bs, Handle(Geom_Surface) surf);
TopoDS_Shape apply_transformation(const TopoDS_Shape& s, const taxonomy::matrix4& t);
TopoDS_Shape apply_transformation(const TopoDS_Shape& s, const gp_GTrsf& t);
TopoDS_Shape apply_transformation(const TopoDS_Shape& s, const gp_Trsf& t);
virtual bool convert_impl(const taxonomy::face*, IfcGeom::ConversionResults&);
virtual bool convert_impl(const taxonomy::solid*, IfcGeom::ConversionResults&);
virtual bool convert_impl(const taxonomy::shell*, IfcGeom::ConversionResults&);
virtual bool convert_impl(const taxonomy::extrusion*, IfcGeom::ConversionResults&);
virtual bool convert_impl(const taxonomy::boolean_result*, IfcGeom::ConversionResults&);
virtual bool convert_openings(const IfcUtil::IfcBaseEntity* entity, const std::vector<std::pair<taxonomy::item*, ifcopenshell::geometry::taxonomy::matrix4>>& openings,
const IfcGeom::ConversionResults& entity_shapes, const ifcopenshell::geometry::taxonomy::matrix4& entity_trsf, IfcGeom::ConversionResults& cut_shapes);
template <typename T, typename U>
static T convert_xyz(const U& u) {
const auto& vs = u.ccomponents();
return T(vs(0), vs(1), vs(2));
}
MAKE_TYPE_NAME(Kernel)& operator=(const MAKE_TYPE_NAME(Kernel)& other) {
deflection_tolerance = other.deflection_tolerance;
max_faces_to_orient = other.max_faces_to_orient;
ifc_length_unit = other.ifc_length_unit;
ifc_planeangle_unit = other.ifc_planeangle_unit;
modelling_precision = other.modelling_precision;
dimensionality = other.dimensionality;
layerset_first = other.layerset_first;
no_wire_intersection_check = other.no_wire_intersection_check;
no_wire_intersection_tolerance = other.no_wire_intersection_tolerance;
precision_factor = other.precision_factor;
boolean_debug_setting = other.boolean_debug_setting;
boolean_attempt_2d = other.boolean_attempt_2d;
placement_rel_to_type_ = other.placement_rel_to_type_;
placement_rel_to_instance_ = other.placement_rel_to_instance_;
disable_boolean_result = other.disable_boolean_result;
offset = other.offset;
rotation = other.rotation;
offset_and_rotation = other.offset_and_rotation;
return *this;
// @todo eliminate
template <typename T, typename U>
static T convert_xyz2(const U& vs) {
return T(vs(0), vs(1), vs(2));
}
void set_offset(const std::array<double, 3>& offset);
void set_rotation(const std::array<double, 4>& rotation);
double get_wire_intersection_tolerance(const TopoDS_Wire&) const;
bool convert_shapes(const IfcUtil::IfcBaseInterface* L, IfcRepresentationShapeItems& result);
IfcGeom::ShapeType shape_type(const IfcUtil::IfcBaseInterface* L);
bool convert_shape(const IfcUtil::IfcBaseInterface* L, TopoDS_Shape& result);
bool convert_wire(const IfcUtil::IfcBaseInterface* L, TopoDS_Wire& result);
bool convert_curve(const IfcUtil::IfcBaseInterface* L, Handle(Geom_Curve)& result);
bool convert_face(const IfcUtil::IfcBaseInterface* L, TopoDS_Shape& result);
bool convert_openings(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const IfcRepresentationShapeItems& entity_shapes, const gp_Trsf& entity_trsf, IfcRepresentationShapeItems& cut_shapes);
bool convert_layerset(const IfcSchema::IfcProduct*, std::vector<Handle_Geom_Surface>&, std::vector<std::shared_ptr<const SurfaceStyle>>&, std::vector<double>&);
bool fold_layers(const IfcSchema::IfcWall*, const IfcRepresentationShapeItems&, const std::vector<Handle_Geom_Surface>&, const std::vector<double>&, std::vector< std::vector<Handle_Geom_Surface> >&);
bool find_wall_end_points(const IfcSchema::IfcWall*, gp_Pnt& start, gp_Pnt& end);
IfcSchema::IfcSurfaceStyleShading* get_surface_style(IfcSchema::IfcRepresentationItem* item);
const IfcSchema::IfcRepresentationItem* find_item_carrying_style(const IfcSchema::IfcRepresentationItem* item);
bool is_identity_transform(IfcUtil::IfcBaseInterface*);
IfcSchema::IfcRelVoidsElement::list::ptr find_openings(IfcSchema::IfcProduct* product);
IfcSchema::IfcRepresentation* find_representation(const IfcSchema::IfcProduct*, const std::string&);
std::pair<std::string, double> initializeUnits(IfcSchema::IfcUnitAssignment*);
IfcGeom::BRepElement* create_brep_for_representation_and_product(
const IteratorSettings&, IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*);
IfcGeom::BRepElement* create_brep_for_processed_representation(
const IteratorSettings&, IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*, IfcGeom::BRepElement*);
const IfcSchema::IfcMaterial* get_single_material_association(const IfcSchema::IfcProduct*);
IfcSchema::IfcRepresentation* representation_mapped_to(const IfcSchema::IfcRepresentation* representation);
IfcSchema::IfcProduct::list::ptr products_represented_by(const IfcSchema::IfcRepresentation*);
std::shared_ptr<const SurfaceStyle> get_style(const IfcSchema::IfcRepresentationItem*);
std::shared_ptr<const SurfaceStyle> get_style(const IfcSchema::IfcMaterial*);
template <typename T> std::pair<IfcSchema::IfcSurfaceStyle*, T*> _get_surface_style(const IfcSchema::IfcStyledItem* si) {
std::vector<IfcSchema::IfcPresentationStyle*> prs_styles;
#ifdef SCHEMA_HAS_IfcStyleAssignmentSelect
aggregate_of_instance::ptr style_assignments = si->Styles();
for (aggregate_of_instance::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) {
// Using IfcPresentationStyleAssignment is deprecated, use the direct assignment of a subtype of IfcPresentationStyle instead.
auto style_k = (*kt)->as<IfcSchema::IfcPresentationStyle>();
if (style_k) {
prs_styles.push_back(style_k);
continue;
}
if (!(*kt)->declaration().is(IfcSchema::IfcPresentationStyleAssignment::Class())) {
continue;
}
IfcSchema::IfcPresentationStyleAssignment* style_assignment = (IfcSchema::IfcPresentationStyleAssignment*) *kt;
Logger::Warning("Deprecated usage of", style_assignment);
// Only in case of 2x3 or old style IfcPresentationStyleAssignment
auto styles = style_assignment->Styles();
#elif defined SCHEMA_HAS_IfcPresentationStyleAssignment
IfcSchema::IfcPresentationStyleAssignment::list::ptr style_assignments = si->Styles();
for (IfcSchema::IfcPresentationStyleAssignment::list::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) {
IfcSchema::IfcPresentationStyleAssignment* style_assignment = *kt;
// Only in case of 2x3 or old style IfcPresentationStyleAssignment
auto styles = style_assignment->Styles();
#else
auto styles = si->Styles();
#endif
for (auto lt = styles->begin(); lt != styles->end(); ++lt) {
auto style_l = (*lt)->as<IfcSchema::IfcPresentationStyle>();
if (style_l) {
prs_styles.push_back(style_l);
}
}
#if defined(SCHEMA_HAS_IfcStyleAssignmentSelect) || defined(SCHEMA_HAS_IfcPresentationStyleAssignment)
}
#endif
for (auto& style : prs_styles) {
if (style->declaration().is(IfcSchema::IfcSurfaceStyle::Class())) {
IfcSchema::IfcSurfaceStyle* surface_style = (IfcSchema::IfcSurfaceStyle*) style;
if (surface_style->Side() != IfcSchema::IfcSurfaceSide::IfcSurfaceSide_NEGATIVE) {
aggregate_of_instance::ptr styles_elements = surface_style->Styles();
for (aggregate_of_instance::it mt = styles_elements->begin(); mt != styles_elements->end(); ++mt) {
if ((*mt)->declaration().is(T::Class())) {
return std::make_pair(surface_style, (T*) *mt);
}
}
}
}
}
return std::make_pair<IfcSchema::IfcSurfaceStyle*, T*>(0,0);
}
template <typename T> std::pair<IfcSchema::IfcSurfaceStyle*, T*> get_surface_style(const IfcSchema::IfcRepresentationItem* representation_item) {
// For certain representation items, most notably boolean operands,
// a style definition might reside on one of its operands.
representation_item = find_item_carrying_style(representation_item);
if (representation_item->as<IfcSchema::IfcStyledItem>()) {
return _get_surface_style<T>(representation_item->as<IfcSchema::IfcStyledItem>());
}
IfcSchema::IfcStyledItem::list::ptr styled_items = representation_item->StyledByItem();
if (styled_items->size()) {
// StyledByItem is a SET [0:1] OF IfcStyledItem, so we return after the first IfcStyledItem:
return _get_surface_style<T>(*styled_items->begin());
}
return std::make_pair<IfcSchema::IfcSurfaceStyle*, T*>(0,0);
}
void purge_cache() {
// Rather hack-ish, but a stopgap solution to keep memory under control
// for large files. SurfaceStyles need to be kept at all costs, as they
// are read later on when serializing Collada files.
#ifndef NO_CACHE
cache = MAKE_TYPE_NAME(Cache)();
#endif
}
void set_conversion_placement_rel_to_type(const IfcParse::declaration* type);
void set_conversion_placement_rel_to_instance(const IfcUtil::IfcBaseEntity* instance);
#include "mapping_kernel_header.i"
virtual void setValue(GeomValue var, double value);
virtual double getValue(GeomValue var) const;
virtual IfcGeom::BRepElement* convert(
const IteratorSettings& settings, IfcUtil::IfcBaseClass* representation,
IfcUtil::IfcBaseClass* product)
{
return create_brep_for_representation_and_product(settings, representation->as<IfcSchema::IfcRepresentation>(), product->as<IfcSchema::IfcProduct>());
}
virtual IfcRepresentationShapeItems convert(IfcUtil::IfcBaseClass* item) {
IfcRepresentationShapeItems items;
bool success = convert_shapes(item, items);
if (!success) {
throw IfcParse::IfcException("Failed to process representation item");
}
return items;
}
virtual bool convert_placement(IfcUtil::IfcBaseClass* item, gp_Trsf& trsf) {
if (item->as<IfcSchema::IfcObjectPlacement>()) {
try {
return convert(item->as<IfcSchema::IfcObjectPlacement>(), trsf);
} catch (std::exception& e) {
Logger::Error(e, item);
} catch (...) {
Logger::Error("Failed processing placement", item);
}
}
return false;
}
};
IfcUtil::IfcBaseClass* MAKE_TYPE_NAME(tesselate_)(const TopoDS_Shape& shape, double deflection);
IfcUtil::IfcBaseClass* MAKE_TYPE_NAME(serialise_)(const TopoDS_Shape& shape, bool advanced);
IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(tesselate_)(const TopoDS_Shape& shape, double deflection);
IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(serialise_)(const TopoDS_Shape& shape, bool advanced);
}
#endif
+68 -1
View File
@@ -1,6 +1,8 @@
#include "base_utils.h"
#include "../ifcparse/IfcLogger.h"
#include "../../../ifcparse/IfcLogger.h"
#include "OpenCascadeConversionResult.h"
#include "boolean_utils.h"
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
@@ -40,6 +42,8 @@
#include <BRepCheck_Analyzer.hxx>
#include <BRepClass3d_SolidClassifier.hxx>
#include <BRepAlgoAPI_Fuse.hxx>
#include <TopTools_IndexedMapOfShape.hxx>
// For axis placements detect equality early in order for the
@@ -739,3 +743,66 @@ bool IfcGeom::util::create_solid_from_faces(const TopTools_ListOfShape& face_lis
return valid_shell;
}
bool IfcGeom::util::flatten_shape_list(const IfcGeom::ConversionResults& shapes, TopoDS_Shape& result, bool fuse, double tol) {
TopoDS_Compound compound;
BRep_Builder builder;
builder.MakeCompound(compound);
result = TopoDS_Shape();
for (IfcGeom::ConversionResults::const_iterator it = shapes.begin(); it != shapes.end(); ++it) {
TopoDS_Shape merged;
const TopoDS_Shape& s = ((ifcopenshell::geometry::OpenCascadeShape*)it->Shape())->shape();
if (fuse) {
util::ensure_fit_for_subtraction(s, merged, tol);
} else {
merged = s;
}
// @todo refactor, also should be GTrsf
const auto& m = it->Placement().ccomponents();
gp_Trsf trsf;
trsf.SetValues(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
m(2, 0), m(2, 1), m(2, 2), m(2, 3)
);
const TopoDS_Shape moved_shape = util::apply_transformation(merged, trsf);
if (shapes.size() == 1) {
result = moved_shape;
return true;
}
if (fuse) {
if (result.IsNull()) {
result = moved_shape;
} else {
BRepAlgoAPI_Fuse brep_fuse(result, moved_shape);
if (brep_fuse.IsDone()) {
TopoDS_Shape fused = brep_fuse;
ShapeFix_Shape fix(result);
fix.Perform();
result = fix.Shape();
bool is_valid = BRepCheck_Analyzer(result).IsValid() != 0;
if (is_valid) {
result = fused;
}
}
}
} else {
builder.Add(compound, moved_shape);
}
}
if (!fuse) {
result = compound;
}
const bool success = !result.IsNull();
return success;
}
@@ -1,6 +1,8 @@
#ifndef BASE_UTILS_H
#define BASE_UTILS_H
#include "../../../ifcgeom/ConversionResult.h"
#include <gp_Ax3.hxx>
#include <gp_Pln.hxx>
#include <gp_Pnt.hxx>
@@ -65,6 +67,8 @@ namespace IfcGeom {
TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_Trsf&);
TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_GTrsf&);
bool flatten_shape_list(const IfcGeom::ConversionResults& shapes, TopoDS_Shape& result, bool fuse, double tol);
}
}
@@ -0,0 +1,145 @@
#include "OpenCascadeKernel.h"
#include "boolean_utils.h"
#include "base_utils.h"
using namespace IfcGeom;
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
namespace {
BOPAlgo_Operation op_to_occt(taxonomy::boolean_result::operation_t t) {
switch (t) {
case taxonomy::boolean_result::UNION: return BOPAlgo_FUSE;
case taxonomy::boolean_result::INTERSECTION: return BOPAlgo_COMMON;
case taxonomy::boolean_result::SUBTRACTION: return BOPAlgo_CUT;
}
}
bool get_single_child(const TopoDS_Shape& s, TopoDS_Shape& child) {
TopoDS_Iterator it(s);
if (!it.More()) {
return false;
}
child = it.Value();
it.Next();
return !it.More();
}
bool is_unbounded_halfspace(const TopoDS_Shape& solid) {
if (solid.ShapeType() != TopAbs_SOLID) {
return false;
}
TopoDS_Shape shell;
if (!get_single_child(solid, shell)) {
return false;
}
TopoDS_Shape face;
if (!get_single_child(shell, face)) {
return false;
}
TopoDS_Iterator it(face);
return !it.More();
}
}
bool OpenCascadeKernel::convert_impl(const taxonomy::boolean_result* br, ConversionResults& results) {
bool valid_result = false;
bool first = true;
const double tol = conv_settings_.getValue(ConversionSettings::GV_PRECISION);
TopoDS_Shape a;
TopTools_ListOfShape b;
taxonomy::style* first_item_style = nullptr;
for (auto& c : br->children) {
IfcGeom::ConversionResults cr;
AbstractKernel::convert(c, cr);
if (first && br->operation == taxonomy::boolean_result::SUBTRACTION) {
// @todo A will be null on union/intersection, intended?
IfcGeom::util::flatten_shape_list(cr, a, false, conv_settings_.getValue(ifcopenshell::geometry::ConversionSettings::GV_PRECISION));
first_item_style = ((taxonomy::geom_item*)c)->surface_style;
if (!first_item_style && c->kind() == taxonomy::COLLECTION) {
// @todo recursively right?
first_item_style = ((taxonomy::geom_item*) ((taxonomy::collection*)c)->children[0])->surface_style;
}
if (conv_settings_.getValue(ConversionSettings::GV_DISABLE_BOOLEAN_RESULT) > 0.0) {
results.emplace_back(IfcGeom::ConversionResult(
(int)br->instance->data().id(),
br->matrix,
new OpenCascadeShape(a),
br->surface_style ? br->surface_style : first_item_style
));
return true;
}
const double first_operand_volume = util::shape_volume(a);
if (first_operand_volume <= ALMOST_ZERO) {
Logger::Message(Logger::LOG_WARNING, "Empty solid for:", c->instance);
}
} else {
for (auto& r : cr) {
auto S = ((OpenCascadeShape*)r.Shape())->shape();
gp_GTrsf trsf;
convert(&r.Placement(), trsf);
// @todo it really confuses me why I cannot use Moved() here instead
S.Location(S.Location() * trsf.Trsf());
if (is_unbounded_halfspace(S)) {
double d;
TopoDS_Shape result;
util::fit_halfspace(a, S, result, d, tol * 1e3);
// #2665 we also set a precision-independent treshold, because in the boolean op routine
// the working fuzziness might still be increased.
if (d < tol * 20. || d < 0.00002) {
Logger::Message(Logger::LOG_WARNING, "Halfspace subtraction yields unchanged volume:", c->instance);
continue;
} else {
S = result;
}
}
b.Append(S);
}
}
first = false;
}
util::boolean_settings bst;
bst.attempt_2d = conv_settings_.getValue(ConversionSettings::GV_BOOLEAN_ATTEMPT_2D) > 0.;
bst.debug = conv_settings_.getValue(ConversionSettings::GV_DEBUG_BOOLEAN) > 0.;
bst.precision = conv_settings_.getValue(ConversionSettings::GV_PRECISION);
TopoDS_Shape r;
if (a.ShapeType() == TopAbs_COMPOUND && TopoDS_Iterator(a).More() && util::is_nested_compound_of_solid(a)) {
TopoDS_Compound C;
BRep_Builder B;
B.MakeCompound(C);
TopoDS_Iterator it(a);
valid_result = true;
for (; it.More(); it.Next()) {
TopoDS_Shape part;
if (util::boolean_operation(bst, it.Value(), b, op_to_occt(br->operation), part)) {
B.Add(C, part);
} else {
valid_result = false;
}
}
r = C;
} else {
valid_result = util::boolean_operation(bst, a, b, op_to_occt(br->operation), r);
}
results.emplace_back(IfcGeom::ConversionResult(
(int) br->instance->data().id(),
br->matrix,
new OpenCascadeShape(r),
br->surface_style ? br->surface_style : first_item_style
));
return valid_result;
}
@@ -1,7 +1,7 @@
#include "boolean_utils.h"
#include "../ifcgeom_schema_agnostic/IfcGeomTree.h"
#include "../ifcgeom_schema_agnostic/base_utils.h"
#include "IfcGeomTree.h"
#include "base_utils.h"
#include <BRepBuilderAPI_Copy.hxx>
#include <TopExp_Explorer.hxx>
@@ -834,6 +834,25 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To
fuzziness = settings.precision / 10.;
}
/*
{
TopoDS_Compound C;
BRep_Builder BB;
BB.MakeCompound(C);
BB.Add(C, a_input);
TopTools_ListIteratorOfListOfShape it(b_input);
for (; it.More(); it.Next()) {
BB.Add(C, it.Value());
}
result = C;
}
return true;
*/
// @todo, it does seem a bit odd, we first triangulate non-planar faces
// to later unify them again. Can we make this a bit more intelligent?
TopoDS_Shape a;
@@ -0,0 +1,59 @@
#include "OpenCascadeKernel.h"
#include <Geom_BSplineSurface.hxx>
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
using namespace IfcGeom;
bool OpenCascadeKernel::convert(const taxonomy::bspline_surface* bs, Handle(Geom_Surface) surf) {
const bool is_rational = !!bs->weights;
TColgp_Array2OfPnt Poles(0, (int)bs->control_points.size() - 1, 0, (int)(*bs->control_points.begin()).size() - 1);
TColStd_Array2OfReal Weights(0, (int)bs->control_points.size() - 1, 0, (int)(*bs->control_points.begin()).size() - 1);
TColStd_Array1OfReal UKnots(0, (int)bs->knots[0].size() - 1);
TColStd_Array1OfReal VKnots(0, (int)bs->knots[1].size() - 1);
TColStd_Array1OfInteger UMults(0, (int)bs->multiplicities[0].size() - 1);
TColStd_Array1OfInteger VMults(0, (int)bs->multiplicities[1].size() - 1);
Standard_Integer UDegree = bs->degree[0];
Standard_Integer VDegree = bs->degree[1];
int i = 0, j;
for (auto it = bs->control_points.begin(); it != bs->control_points.end(); ++it, ++i) {
j = 0;
for (auto jt = (*it).begin(); jt != (*it).end(); ++jt, ++j) {
Poles(i, j) = convert_xyz<gp_Pnt>(*jt);
}
}
i = 0;
for (std::vector<double>::const_iterator it = bs->knots[0].begin(); it != bs->knots[0].end(); ++it, ++i) {
UKnots(i) = *it;
}
i = 0;
for (std::vector<double>::const_iterator it = bs->knots[1].begin(); it != bs->knots[1].end(); ++it, ++i) {
VKnots(i) = *it;
}
i = 0;
for (std::vector<int>::const_iterator it = bs->multiplicities[0].begin(); it != bs->multiplicities[0].end(); ++it, ++i) {
UMults(i) = *it;
}
i = 0;
for (std::vector<int>::const_iterator it = bs->multiplicities[1].begin(); it != bs->multiplicities[1].end(); ++it, ++i) {
VMults(i) = *it;
}
if (is_rational) {
for (auto it = bs->weights->begin(); it != bs->weights->end(); ++it, ++i) {
j = 0;
for (auto jt = (*it).begin(); jt != (*it).end(); ++jt, ++j) {
Weights(i, j) = *jt;
}
}
surf = new Geom_BSplineSurface(Poles, Weights, UKnots, VKnots, UMults, VMults, UDegree, VDegree);
} else {
surf = new Geom_BSplineSurface(Poles, UKnots, VKnots, UMults, VMults, UDegree, VDegree);
}
return true;
}
@@ -0,0 +1,87 @@
#include "OpenCascadeKernel.h"
#include <BRepPrimAPI_MakePrism.hxx>
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
using namespace IfcGeom;
bool OpenCascadeKernel::convert(const taxonomy::extrusion* extrusion, TopoDS_Shape& shape) {
const double& height = extrusion->depth;
if (height < conv_settings_.getValue(ConversionSettings::GV_PRECISION)) {
Logger::Error("Non-positive extrusion height encountered for:", extrusion->instance);
return false;
}
TopoDS_Shape face;
if (!convert(&extrusion->basis, face)) {
return false;
}
/*
// @todo we need to decide whether the matrix is kept on the taxonomy node or
// move the TopoDS_Shape, but obviously not both.
gp_GTrsf gtrsf;
if (!convert(&extrusion->matrix, gtrsf)) {
Logger::Error("Unable to move extrusion");
}
auto trsf = gtrsf.Trsf();
*/
const auto& fs = extrusion->direction.ccomponents();
gp_Dir dir(fs(0), fs(1), fs(2));
shape.Nullify();
if (face.ShapeType() == TopAbs_COMPOUND) {
// For compounds (most likely the result of a IfcCompositeProfileDef)
// create a compound solid shape.
TopExp_Explorer exp(face, TopAbs_FACE);
TopoDS_CompSolid compound;
BRep_Builder builder;
builder.MakeCompSolid(compound);
int num_faces_extruded = 0;
for (; exp.More(); exp.Next(), ++num_faces_extruded) {
builder.Add(compound, BRepPrimAPI_MakePrism(exp.Current(), height*dir));
}
if (num_faces_extruded) {
shape = compound;
}
}
if (shape.IsNull()) {
shape = BRepPrimAPI_MakePrism(face, height*dir);
}
/*
if (!shape.IsNull()) {
// IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D
// and therefore has a unit scale factor
shape.Move(trsf);
}
*/
return !shape.IsNull();
}
bool OpenCascadeKernel::convert_impl(const taxonomy::extrusion* extrusion, IfcGeom::ConversionResults& results) {
TopoDS_Shape shape;
if (!convert(extrusion, shape)) {
return false;
}
results.emplace_back(ConversionResult(
extrusion->instance->data().id(),
extrusion->matrix,
new OpenCascadeShape(shape),
extrusion->surface_style
));
return true;
}
+335
View File
@@ -0,0 +1,335 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include <gp_Vec.hxx>
#include <gp_Dir.hxx>
#include <gp_Pln.hxx>
#include <Geom_Line.hxx>
#include <Geom_Plane.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Face.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS_Iterator.hxx>
#include <ShapeFix_Shape.hxx>
#include <ShapeFix_ShapeTolerance.hxx>
#include <BRep_Tool.hxx>
#include <TopTools_DataMapOfShapeInteger.hxx>
#include <BRepLib_FindSurface.hxx>
#include <ShapeExtend_MsgRegistrator.hxx>
#include <Message_Msg.hxx>
#include <ShapeFix_Edge.hxx>
#include <BRepPrimAPI_MakeHalfSpace.hxx>
#include "OpenCascadeKernel.h"
#include "face_definition.h"
#include "wire_utils.h"
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
using namespace IfcGeom;
using namespace IfcGeom::util;
bool OpenCascadeKernel::convert(const taxonomy::face* face, TopoDS_Shape& result) {
auto bounds = face->children_as<taxonomy::loop>();
face_definition fd;
const bool is_face_surface = false; /* todo */
/*
if (is_face_surface) {
IfcSchema::IfcFaceSurface* fs = (IfcSchema::IfcFaceSurface*) l;
fs->FaceSurface();
// FIXME: Surfaces are interpreted as a TopoDS_Shape
TopoDS_Shape surface_shape;
if (!convert_shape(fs->FaceSurface(), surface_shape)) return false;
// FIXME: Assert this obtaines the only face
TopExp_Explorer exp(surface_shape, TopAbs_FACE);
if (!exp.More()) return false;
TopoDS_Face surface = TopoDS::Face(exp.Current());
fd.surface() = BRep_Tool::Surface(surface);
}
*/
const int num_bounds = bounds.size();
int num_outer_bounds = 0;
for (auto& bound : bounds) {
if (bound->external.get_value_or(false)) {
num_outer_bounds++;
}
}
// The number of outer bounds should be one according to the schema. Also Open Cascade
// expects this, but it is not strictly checked. Regardless, if the number is greater,
// the face will still be processed as long as there are no holes. A compound of faces
// is returned in that case.
if (num_bounds > 1 && num_outer_bounds > 1 && num_bounds != num_outer_bounds) {
Logger::Message(Logger::LOG_ERROR, "Invalid configuration of boundaries for:", face->instance);
return false;
}
if (num_outer_bounds > 1) {
Logger::Message(Logger::LOG_WARNING, "Multiple outer boundaries for:", face->instance);
fd.all_outer() = true;
}
TopTools_DataMapOfShapeInteger wire_senses;
for (int process_interior = 0; process_interior <= 1; ++process_interior) {
for (auto& bound : bounds) {
bool same_sense = true; /* todo bound->Orientation(); */
const bool is_interior =
!bound->external.get_value_or(false) &&
(num_bounds > 1) &&
(num_outer_bounds < num_bounds);
// The exterior face boundary is processed first
if (is_interior == !process_interior) continue;
TopoDS_Wire wire;
if (faceset_helper_ && bound->is_polyhedron()) {
if (!faceset_helper_->wire(bound, wire)) {
Logger::Message(Logger::LOG_WARNING, "Face boundary loop not included", bound->instance);
continue;
}
} else if (!convert(bound, wire)) {
Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary loop", bound->instance);
return false;
}
if (!same_sense) {
wire.Reverse();
}
wire_senses.Bind(wire.Oriented(TopAbs_FORWARD), same_sense ? TopAbs_FORWARD : TopAbs_REVERSED);
fd.wires().emplace_back(wire);
}
}
if (fd.wires().empty()) {
Logger::Warning("Face with no boundaries", face->instance);
return false;
}
if (fd.surface().IsNull()) {
// Use the first wire to find a plane manually for polygonal wires
const TopoDS_Wire& wire = fd.wires().front();
if (is_polyhedron(wire)) {
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;
fd.surface() = new Geom_Plane(c1->Position().Location(), n);
}
} else {
gp_Pln pln;
if (approximate_plane_through_wire(wire, pln, precision_)) {
fd.surface() = new Geom_Plane(pln);
}
}
}
}
if (fd.surface().IsNull()) {
// BRepLib_FindSurface is used in case no surface is found or provided
const TopoDS_Wire& wire = fd.wires().front();
BRepLib_FindSurface fs(wire, precision_, true, true);
if (fs.Found()) {
fd.surface() = fs.Surface();
ShapeFix_ShapeTolerance ftol;
ftol.SetTolerance(wire, fs.ToleranceReached(), TopAbs_WIRE);
}
}
TopTools_ListOfShape face_list;
if (fd.surface().IsNull()) {
// The set of wires is triangulated in case no surface can be found
Logger::Message(Logger::LOG_WARNING, "Triangulating face boundaries for face", face->instance);
if (fd.all_outer()) {
for (const auto& w : fd.wires()) {
TopTools_ListOfShape fl;
triangulate_wire({ w }, fl);
face_list.Append(fl);
}
} else {
triangulate_wire(fd.wires(), face_list);
}
} else if (!fd.all_outer()) {
BRepBuilderAPI_MakeFace mf(fd.surface(), fd.outer_wire());
if (mf.IsDone()) {
// Is this necessary
TopoDS_Face f = mf.Face();
mf.Init(f);
for (auto it = fd.inner_wires().first; it != fd.inner_wires().second; ++it) {
mf.Add(*it);
}
face_list.Append(mf.Face());
}
} else {
for (const auto& w : fd.wires()) {
BRepBuilderAPI_MakeFace mf(fd.surface(), w);
if (mf.IsDone()) {
face_list.Append(mf.Face());
}
}
}
if (!fd.surface().IsNull()) {
// Some fixes for orientation and p-curves. If we have no surface, it
// means the face has been triangulated in which case none of these
// fixes are necessary.
if (fd.surface()->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
// In case of (non-planar) face surface, p-curves need to be computed.
// For planar faces, Open Cascade generates p-curves on the fly.
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
// Small chance there are multiple faces
const TopoDS_Face& occ_face = TopoDS::Face(it.Value());
for (TopExp_Explorer exp2(occ_face, TopAbs_EDGE); exp2.More(); exp2.Next()) {
const TopoDS_Edge& edge = TopoDS::Edge(exp2.Current());
ShapeFix_Edge fix_edge;
fix_edge.FixAddPCurve(edge, occ_face, false, precision_);
}
}
}
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
const TopoDS_Face& occ_face = TopoDS::Face(it.Value());
ShapeFix_Face sfs(TopoDS::Face(occ_face));
TopTools_DataMapOfShapeListOfShape wire_map;
sfs.FixOrientation(wire_map);
TopoDS_Iterator jt(occ_face, false);
for (; jt.More(); jt.Next()) {
const TopoDS_Wire& w = TopoDS::Wire(jt.Value());
// tfk: @todo if wire_map contains w, I would assume wire_senses also contains w,
// this is not the case in github issue #405.
if (wire_map.IsBound(w) && wire_senses.IsBound(w)) {
const TopTools_ListOfShape& shapes = wire_map.Find(w);
TopTools_ListIteratorOfListOfShape kt(shapes);
for (; kt.More(); kt.Next()) {
// Apparently the wire got reversed, so register it with opposite orientation in the map
wire_senses.Bind(kt.Value(), wire_senses.Find(w) == TopAbs_FORWARD ? TopAbs_REVERSED : TopAbs_FORWARD);
}
}
}
it.Value() = sfs.Face();
}
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
TopoDS_Face& occ_face = TopoDS::Face(it.Value());
bool all_reversed = true;
TopoDS_Iterator jt(occ_face, false);
for (; jt.More(); jt.Next()) {
const TopoDS_Wire& w = TopoDS::Wire(jt.Value());
if (!wire_senses.IsBound(w.Oriented(TopAbs_FORWARD)) || (w.Orientation() == wire_senses.Find(w.Oriented(TopAbs_FORWARD)))) {
all_reversed = false;
}
}
if (all_reversed) {
occ_face.Reverse();
}
}
}
if (face_list.Extent() == 0) {
return false;
} else if (face_list.Extent() > 1) {
TopoDS_Compound compound;
BRep_Builder builder;
builder.MakeCompound(compound);
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
TopoDS_Face& occ_face = TopoDS::Face(it.Value());
builder.Add(compound, occ_face);
}
result = compound;
} else {
result = face_list.First();
}
return true;
}
bool OpenCascadeKernel::convert_impl(const taxonomy::face* face, IfcGeom::ConversionResults& results) {
throw std::runtime_error("Root-level face not expected");
/*
// Root level faces are only encountered in case of half spaces
// @todo this is not true, halfspace will be solid>face
if (face->basis == nullptr) {
Logger::Error("Half space without underlying surface:", face->instance);
return false;
}
if (face->basis->kind() != taxonomy::PLANE) {
Logger::Message(Logger::LOG_ERROR, "Unsupported BaseSurface:", face->basis->instance);
return false;
}
// @todo boundary
const auto& m = ((taxonomy::geom_item*)face->basis)->matrix.ccomponents();
gp_Pln pln(convert_xyz2<gp_Pnt>(m.col(3)), convert_xyz2<gp_Dir>(m.col(2)));
const gp_Pnt pnt = pln.Location().Translated(face->orientation.get_value_or(false) ? -pln.Axis().Direction() : pln.Axis().Direction());
TopoDS_Shape shape = BRepPrimAPI_MakeHalfSpace(BRepBuilderAPI_MakeFace(pln), pnt).Solid();
results.emplace_back(ConversionResult(
face->instance->data().id(),
new OpenCascadeShape(shape),
face->surface_style
));
return true;
*/
}
@@ -0,0 +1,274 @@
#include "OpenCascadeKernel.h"
#include "IfcGeomTree.h"
#include "wire_utils.h"
namespace {
void find_neighbours(IfcGeom::impl::tree<int>& tree, std::vector<std::unique_ptr<gp_Pnt>>& pnts, std::set<int>& visited, int p, double eps) {
visited.insert(p);
Bnd_Box b;
b.Set(*pnts[p].get());
b.Enlarge(eps);
std::vector<int> js = tree.select_box(b, false);
for (int j : js) {
visited.insert(j);
#ifdef FACESET_HELPER_RECURSIVE
if (visited.find(j) == visited.end()) {
// @todo, making this recursive removes the dependence on the initial ordering, but will
// likely result in empty results when all vertices are within 1 eps from another point.
find_neighbours(tree, pnts, visited, j, eps);
}
#endif
}
}
}
IfcGeom::OpenCascadeKernel::faceset_helper::faceset_helper(
OpenCascadeKernel* kernel,
const taxonomy::shell* shell
)
: kernel_(kernel)
, non_manifold_(false)
{
// @todo use pointers?
std::vector<taxonomy::point3> points;
std::vector<taxonomy::loop*> loops;
for (auto& f : shell->children_as<taxonomy::face>()) {
for (auto& l : f->children_as<taxonomy::loop>()) {
loops.push_back(l);
for (auto& e : l->children_as<taxonomy::edge>()) {
// @todo make sure only cartesian points are provided here
points.push_back(boost::get<taxonomy::point3>(e->start));
}
}
}
std::vector<std::unique_ptr<gp_Pnt>> pnts(points.size());
std::vector<TopoDS_Vertex> vertices(pnts.size());
IfcGeom::impl::tree<int> tree;
BRep_Builder B;
Bnd_Box box;
for (size_t i = 0; i < points.size(); ++i) {
gp_Pnt* p = new gp_Pnt(convert_xyz<gp_Pnt>(points[i]));
pnts[i].reset(p);
B.MakeVertex(vertices[i], *p, Precision::Confusion());
tree.add(i, vertices[i]);
box.Add(*p);
}
// Use the bbox diagonal to influence local epsilon
// double bdiff = std::sqrt(box.SquareExtent());
// @todo the bounding box diagonal is not used (see above)
// because we're explicitly interested in the miminal
// dimension of the element to limit the tolerance (for sheet-
// like elements for example). But the way below is very
// dependent on orientation due to the usage of the
// axis-aligned bounding box. Use PCA to find three non-aligned
// set of dimensions and use the one with the smallest eigenvalue.
// Find the minimal bounding box edge
double bmin[3], bmax[3];
box.Get(bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2]);
double bdiff = std::numeric_limits<double>::infinity();
for (size_t i = 0; i < 3; ++i) {
const double d = bmax[i] - bmin[i];
if (d > kernel->settings().getValue(ConversionSettings::GV_PRECISION) * 10. && d < bdiff) {
bdiff = d;
}
}
eps_ = kernel->settings().getValue(ConversionSettings::GV_PRECISION) * 10. * (std::min)(1.0, bdiff);
size_t loops_removed, non_manifold, duplicate_faces;
std::map<std::pair<int, int>, int> edge_use;
for (int i = 0; i < 3; ++i) {
// Some times files, have large tolerance values specified collapsing too many vertices.
// This case we detect below and re-run the loop with smaller epsilon. Normally
// the body of this loop would only be executed once.
loops_removed = 0;
non_manifold = 0;
duplicate_faces = 0;
vertex_mapping_.clear();
duplicates_.clear();
edge_use.clear();
if (eps_ < Precision::Confusion()) {
// occt uses some hard coded precision values, don't go smaller than that.
// @todo, can be reset though with BRepLib::Precision(double)
eps_ = Precision::Confusion();
}
for (int pnt_i = 0; pnt_i < (int)pnts.size(); ++pnt_i) {
if (pnts[pnt_i]) {
std::set<int> vs;
find_neighbours(tree, pnts, vs, pnt_i, eps_);
for (int v : vs) {
auto& pt = points[v];
// NB: insert() ignores duplicate keys
// v-1?
// @todo this reliable also in case of tesselations?
vertex_mapping_.insert({ pt.instance->data().id(), pnt_i });
}
}
}
std::set<std::tuple<double, double, double>> unique;
for (int pnt_i = 0; pnt_i < (int)pnts.size(); ++pnt_i) {
if (pnts[pnt_i]) {
unique.insert(std::make_tuple(
(*pnts[pnt_i]).X(),
(*pnts[pnt_i]).Y(),
(*pnts[pnt_i]).Z()
));
}
}
if (unique.size() != vertex_mapping_.size()) {
Logger::Notice("Collapsed vertices from " + std::to_string(pnts.size()) + " (" + std::to_string(unique.size()) + " unique) to " + std::to_string(vertex_mapping_.size()));
}
typedef std::array<int, 2> edge_t;
typedef std::set<edge_t> edge_set_t;
std::set<edge_set_t> edge_sets;
for (auto& loop : loops) {
std::vector<std::pair<int, int> > segments;
edge_set_t segment_set;
loop_(loop, [&segments, &segment_set](int C, int D, bool) {
segment_set.insert(edge_t{ C,D });
segments.push_back(std::make_pair(C, D));
});
if (edge_sets.find(segment_set) != edge_sets.end()) {
duplicate_faces++;
// @todo does this work with tesselated face sets, will they have an associated instance? Guess not.
duplicates_.insert(loop->instance->data().id());
continue;
}
edge_sets.insert(segment_set);
if (segments.size() >= 3) {
for (auto& p : segments) {
edge_use[p] ++;
}
} else {
loops_removed += 1;
}
}
if (edge_use.size() != 0) {
break;
} else {
eps_ /= 10.;
}
}
for (auto& p : edge_use) {
int a, b;
std::tie(a, b) = p.first;
edges_[p.first] = BRepBuilderAPI_MakeEdge(vertices[a], vertices[b]);
if (p.second != 2) {
non_manifold += 1;
}
}
if (duplicates_.size() || loops_removed || (non_manifold && shell->closed.get_value_or(false))) {
Logger::Warning(boost::lexical_cast<std::string>(duplicate_faces) + " duplicate faces removed, " + boost::lexical_cast<std::string>(loops_removed) + " degenerate loops eliminated and " + boost::lexical_cast<std::string>(non_manifold) + " non-manifold edges");
}
}
void IfcGeom::OpenCascadeKernel::faceset_helper::loop_(const taxonomy::loop* ps, const std::function<void(int, int, bool)>& callback) {
if (ps->children.size() < 3) {
return;
}
auto a = boost::get<taxonomy::point3>(((taxonomy::edge*) ps->children.back())->start).instance;
auto A = a->data().id();
for (auto& b : ps->children) {
auto B = boost::get<taxonomy::point3>(((taxonomy::edge*) b)->start).instance->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;
}
}
}
bool IfcGeom::OpenCascadeKernel::faceset_helper::edge(int A, int B, TopoDS_Edge& e) {
auto it = edges_.find({ A, B });
if (it == edges_.end()) {
return false;
}
e = it->second;
return true;
}
bool IfcGeom::OpenCascadeKernel::faceset_helper::wire(const taxonomy::loop* loop, TopoDS_Wire& w) {
TopTools_ListOfShape ws;
if (!wires(loop, ws)) {
return false;
}
util::select_largest(ws, w);
return true;
}
bool IfcGeom::OpenCascadeKernel::faceset_helper::wires(const taxonomy::loop* loop, TopTools_ListOfShape& wires) {
if (duplicates_.find(loop->instance->data().id()) != duplicates_.end()) {
return false;
}
TopoDS_Wire wire;
BRep_Builder builder;
builder.MakeWire(wire);
int count = 0;
loop_(loop, [this, &builder, &wire, &count](int A, int B, bool fwd) {
TopoDS_Edge e;
if (edge(A, B, e)) {
if (!fwd) {
e.Reverse();
}
builder.Add(wire, e);
count += 1;
}
});
if (count >= 3) {
wire.Closed(true);
TopTools_ListOfShape results;
if (kernel_->settings().getValue(ConversionSettings::GV_NO_WIRE_INTERSECTION_CHECK) < 0. && util::wire_intersections(wire, results, {kernel_->settings().getValue(ConversionSettings::GV_NO_WIRE_INTERSECTION_CHECK) < 0., kernel_->settings().getValue(ConversionSettings::GV_NO_WIRE_INTERSECTION_TOLERANCE) < 0., 0., kernel_->settings().getValue(ConversionSettings::GV_PRECISION)})) {
Logger::Warning("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected");
non_manifold_ = true;
wires = results;
} else {
wires.Append(wire);
}
return true;
} else {
return false;
}
}
IfcGeom::OpenCascadeKernel::faceset_helper::~faceset_helper() {
// @todo this is super ugly, but how else can we be notified that the unique_ptr goes out of scope?
// Perhaps just supply a custom std::deleter?
kernel_->faceset_helper_ = nullptr;
}
+24 -21
View File
@@ -1,9 +1,10 @@
#include "layerset.h"
#include "OpenCascadeConversionResult.h"
#include "base_utils.h"
#include "boolean_utils.h"
#include "../ifcparse/IfcLogger.h"
#include "../../../ifcparse/IfcLogger.h"
#include <BRep_Tool.hxx>
@@ -30,6 +31,8 @@
#include <ShapeFix_Shape.hxx>
#include <NCollection_IncAllocator.hxx>
using namespace ifcopenshell::geometry;
namespace {
void subshapes(const TopoDS_Shape& in, std::list<TopoDS_Shape>& out) {
@@ -161,7 +164,7 @@ namespace {
}
bool IfcGeom::util::apply_folded_layerset(const IfcRepresentationShapeItems& items, const std::vector< std::vector<Handle_Geom_Surface> >& surfaces, const std::vector<std::shared_ptr<const SurfaceStyle>>& styles, IfcRepresentationShapeItems& result, double tol) {
bool IfcGeom::util::apply_folded_layerset(const ConversionResults& items, const std::vector< std::vector<Handle_Geom_Surface> >& surfaces, const std::vector<ifcopenshell::geometry::taxonomy::style>& styles, ConversionResults& result, double tol) {
Bnd_Box bb;
TopoDS_Shape input;
flatten_shape_list(items, input, false, tol);
@@ -243,11 +246,11 @@ bool IfcGeom::util::apply_folded_layerset(const IfcRepresentationShapeItems& ite
} else if (shells.Extent() == 1) {
for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) {
for (ConversionResults::const_iterator it = items.begin(); it != items.end(); ++it) {
TopoDS_Shape a, b;
if (split_solid_by_shell(it->Shape(), shells.First(), a, b, tol)) {
result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), b, !!styles[0] ? styles[0] : it->StylePtr()));
result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), a, !!styles[1] ? styles[1] : it->StylePtr()));
if (split_solid_by_shell(((OpenCascadeShape*)it->Shape())->shape(), shells.First(), a, b, tol)) {
result.push_back(ConversionResult(it->ItemId(), it->Placement(), new OpenCascadeShape(b), &(!!styles[0].diffuse ? styles[0] : it->Style())));
result.push_back(ConversionResult(it->ItemId(), it->Placement(), new OpenCascadeShape(a), &(!!styles[1].diffuse ? styles[1] : it->Style())));
} else {
continue;
}
@@ -257,16 +260,16 @@ bool IfcGeom::util::apply_folded_layerset(const IfcRepresentationShapeItems& ite
} else {
for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) {
for (ConversionResults::const_iterator it = items.begin(); it != items.end(); ++it) {
const TopoDS_Shape& s = it->Shape();
const TopoDS_Shape& s = ((OpenCascadeShape*)it->Shape())->shape();
TopoDS_Solid sld;
ensure_fit_for_subtraction(s, sld, tol);
std::vector<TopoDS_Shape> slices;
if (split(it->Shape(), shells, tol, slices) && slices.size() == styles.size()) {
if (split(s, shells, tol, slices) && slices.size() == styles.size()) {
for (size_t i = 0; i < slices.size(); ++i) {
result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), slices[i], !!styles[i] ? styles[i] : it->StylePtr()));
result.push_back(ConversionResult(it->ItemId(), it->Placement(), new OpenCascadeShape(slices[i]), &(!!styles[i].diffuse ? styles[i] : it->Style())));
}
} else {
return false;
@@ -279,18 +282,18 @@ bool IfcGeom::util::apply_folded_layerset(const IfcRepresentationShapeItems& ite
}
bool IfcGeom::util::apply_layerset(const IfcRepresentationShapeItems& items, const std::vector<Handle_Geom_Surface>& surfaces, const std::vector<std::shared_ptr<const SurfaceStyle>>& styles, IfcRepresentationShapeItems& result, double tol) {
bool IfcGeom::util::apply_layerset(const ConversionResults& items, const std::vector<Handle_Geom_Surface>& surfaces, const std::vector<ifcopenshell::geometry::taxonomy::style>& styles, ConversionResults& result, double tol) {
if (surfaces.size() < 3) {
return false;
} else if (surfaces.size() == 3) {
for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) {
for (ConversionResults::const_iterator it = items.begin(); it != items.end(); ++it) {
TopoDS_Shape a, b;
if (split_solid_by_surface(it->Shape(), surfaces[1], a, b, tol)) {
result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), b, !!styles[0] ? styles[0] : it->StylePtr()));
result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), a, !!styles[1] ? styles[1] : it->StylePtr()));
if (split_solid_by_surface(((OpenCascadeShape*)it->Shape())->shape(), surfaces[1], a, b, tol)) {
result.push_back(ConversionResult(it->ItemId(), it->Placement(),new OpenCascadeShape(b), &(!!styles[0].diffuse ? styles[0] : it->Style())));
result.push_back(ConversionResult(it->ItemId(), it->Placement(),new OpenCascadeShape(a), &(!!styles[1].diffuse ? styles[1] : it->Style())));
} else {
continue;
}
@@ -304,7 +307,7 @@ bool IfcGeom::util::apply_layerset(const IfcRepresentationShapeItems& items, con
// Determine whether sequence of surfaces is consistent with surface normal, so that
// layer operations are applied in the correct order. This seems to be always the case.
Bnd_Box bb;
for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) {
for (ConversionResults::const_iterator it = items.begin(); it != items.end(); ++it) {
BRepBndLib::Add(it->Shape(), bb);
}
@@ -329,9 +332,9 @@ bool IfcGeom::util::apply_layerset(const IfcRepresentationShapeItems& items, con
mass.ChangeCoord() += n1.XYZ();
*/
for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) {
for (ConversionResults::const_iterator it = items.begin(); it != items.end(); ++it) {
const TopoDS_Shape& s = it->Shape();
const TopoDS_Shape& s = ((OpenCascadeShape*)it->Shape())->shape();
TopoDS_Solid sld;
ensure_fit_for_subtraction(s, sld, tol);
@@ -350,14 +353,14 @@ bool IfcGeom::util::apply_layerset(const IfcRepresentationShapeItems& items, con
/*
// enable this is you want to see how IfcOpenShell has placed the layer surfaces
for (auto& x : operands) {
result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), x, nullptr));
result.push_back(ConversionResult(it->ItemId(), it->Placement(), x, nullptr));
}
*/
std::vector<TopoDS_Shape> slices;
if (split(it->Shape(), operands, tol, slices) && slices.size() == styles.size()) {
if (split(s, operands, tol, slices) && slices.size() == styles.size()) {
for (size_t i = 0; i < slices.size(); ++i) {
result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), slices[i], !!styles[i] ? styles[i] : it->StylePtr()));
result.push_back(ConversionResult(it->ItemId(), it->Placement(), new OpenCascadeShape(slices[i]), &(!!styles[i].diffuse ? styles[i] : it->Style())));
}
} else {
return false;
+3 -3
View File
@@ -1,7 +1,7 @@
#ifndef LAYERSET_H
#define LAYERSET_H
#include "IfcRepresentationShapeItem.h"
#include "../../ConversionResult.h"
#include <Geom_Surface.hxx>
@@ -10,9 +10,9 @@
namespace IfcGeom {
namespace util {
bool apply_layerset(const IfcRepresentationShapeItems&, const std::vector<Handle_Geom_Surface>&, const std::vector<std::shared_ptr<const SurfaceStyle>>&, IfcRepresentationShapeItems&, double tol);
bool apply_layerset(const ConversionResults&, const std::vector<Handle_Geom_Surface>&, const std::vector<ifcopenshell::geometry::taxonomy::style>&, ConversionResults&, double tol);
bool apply_folded_layerset(const IfcRepresentationShapeItems&, const std::vector< std::vector<Handle_Geom_Surface> >&, const std::vector<std::shared_ptr<const SurfaceStyle>>&, IfcRepresentationShapeItems&, double tol);
bool apply_folded_layerset(const ConversionResults&, const std::vector< std::vector<Handle_Geom_Surface> >&, const std::vector<ifcopenshell::geometry::taxonomy::style>&, ConversionResults&, double tol);
bool split_solid_by_surface(const TopoDS_Shape&, const Handle_Geom_Surface&, TopoDS_Shape&, TopoDS_Shape&, double tol);
+236
View File
@@ -0,0 +1,236 @@
#include "OpenCascadeKernel.h"
#include "wire_builder.h"
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <BRepAdaptor_CompCurve.hxx>
#include <BRepAdaptor_HCompCurve.hxx>
#include <Approx_Curve3d.hxx>
#include <ShapeFix_ShapeTolerance.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <gp_Pnt.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_Array1OfInteger.hxx>
#include <Geom_BSplineCurve.hxx>
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
using namespace IfcGeom;
using namespace IfcGeom::util;
namespace {
typedef boost::variant<Handle(Geom_Curve), TopoDS_Wire> curve_creation_visitor_result_type;
curve_creation_visitor_result_type convert_curve(OpenCascadeKernel* kernel, const taxonomy::item* curve);
struct curve_creation_visitor {
OpenCascadeKernel* kernel;
curve_creation_visitor_result_type result;
curve_creation_visitor_result_type operator()(const taxonomy::bspline_curve& bc) {
const bool is_rational = !!bc.weights;
TColgp_Array1OfPnt Poles(0, bc.control_points.size() - 1);
TColStd_Array1OfReal Weights(0, bc.control_points.size() - 1);
TColStd_Array1OfReal Knots(0, (int)bc.knots.size() - 1);
TColStd_Array1OfInteger Mults(0, (int)bc.knots.size() - 1);
Standard_Integer Degree = bc.degree;
Standard_Boolean Periodic = false;
// @tfk: it appears to be wrong to expect a period curve when the curve is closed, see #586
// Standard_Boolean Periodic = l->ClosedCurve();
int i;
if (is_rational) {
i = 0;
for (auto it = bc.weights->begin(); it != bc.weights->end(); ++it, ++i) {
Weights(i) = *it;
}
}
i = 0;
for (auto it = bc.control_points.begin(); it != bc.control_points.end(); ++it, ++i) {
Poles(i) = OpenCascadeKernel::convert_xyz<gp_Pnt>(*it);
}
i = 0;
for (auto it = bc.multiplicities.begin(); it != bc.multiplicities.end(); ++it, ++i) {
Mults(i) = *it;
}
i = 0;
for (auto it = bc.knots.begin(); it != bc.knots.end(); ++it, ++i) {
Knots(i) = *it;
}
if (is_rational) {
return result = Handle(Geom_Curve)(new Geom_BSplineCurve(Poles, Weights, Knots, Mults, Degree, Periodic));
} else {
return result = Handle(Geom_Curve)(new Geom_BSplineCurve(Poles, Knots, Mults, Degree, Periodic));
}
}
curve_creation_visitor_result_type operator()(const taxonomy::line& l) {
const auto& m = l.matrix.ccomponents();
return result = Handle(Geom_Curve)(new Geom_Line(OpenCascadeKernel::convert_xyz2<gp_Pnt>(m.col(3)), OpenCascadeKernel::convert_xyz2<gp_Dir>(m.col(0))));
}
curve_creation_visitor_result_type operator()(const taxonomy::circle& c) {
const auto& m = c.matrix.ccomponents();
return result = Handle(Geom_Curve)(new Geom_Circle(gp_Ax2(OpenCascadeKernel::convert_xyz2<gp_Pnt>(m.col(3)), OpenCascadeKernel::convert_xyz2<gp_Dir>(m.col(2)), OpenCascadeKernel::convert_xyz2<gp_Dir>(m.col(0))), c.radius));
}
curve_creation_visitor_result_type operator()(const taxonomy::ellipse& e) {
const auto& m = e.matrix.ccomponents();
return result = Handle(Geom_Curve)(new Geom_Ellipse(gp_Ax2(OpenCascadeKernel::convert_xyz2<gp_Pnt>(m.col(3)), OpenCascadeKernel::convert_xyz2<gp_Dir>(m.col(2)), OpenCascadeKernel::convert_xyz2<gp_Dir>(m.col(0))), e.radius, e.radius2));
}
curve_creation_visitor_result_type operator()(const taxonomy::loop& l) {
TopoDS_Wire wire;
kernel->convert(&l, wire);
return result = wire;
}
curve_creation_visitor_result_type operator()(const taxonomy::edge& e) {
// @todo for polyloops/-lines we should probably construct edges based on correct oriented TopoDS_Vertex instead.
if (e.start.which() != e.end.which()) {
throw std::runtime_error("Different trim types not supported");
}
TopoDS_Edge E;
if (e.basis) {
auto crv_or_wire = convert_curve(kernel, e.basis);
Handle(Geom_Curve) curve;
if (crv_or_wire.which() == 0) {
curve = boost::get<Handle(Geom_Curve)>(crv_or_wire);
} else {
// @todo
const double precision_ = 1.e-5;
Logger::Warning("Approximating BasisCurve due to possible discontinuities", e.instance);
BRepAdaptor_CompCurve cc(boost::get<TopoDS_Wire>(crv_or_wire), true);
Handle(Adaptor3d_HCurve) hcc = Handle(Adaptor3d_HCurve)(new BRepAdaptor_HCompCurve(cc));
// @todo, arbitrary numbers here, note they cannot be too high as contiguous memory is allocated based on them.
Approx_Curve3d approx(hcc, precision_, GeomAbs_C0, 10, 10);
curve = approx.Curve();
}
const bool reversed = !((taxonomy::geom_item*)e.basis)->orientation.get_value_or(true);
const bool is_conic = e.basis->kind() == taxonomy::ELLIPSE || e.basis->kind() == taxonomy::CIRCLE;
// @todo, copy over logic from previous IfcTrimmedCurve handling
if (e.start.which() == 0) {
auto p1 = OpenCascadeKernel::convert_xyz<gp_Pnt>(boost::get<taxonomy::point3>(e.start));
auto p2 = OpenCascadeKernel::convert_xyz<gp_Pnt>(boost::get<taxonomy::point3>(e.end));
if (reversed) {
std::swap(p1, p2);
}
E = BRepBuilderAPI_MakeEdge(curve, p1, p2).Edge();
} else {
auto v1 = boost::get<double>(e.start);
auto v2 = boost::get<double>(e.end);
if (reversed) {
std::swap(v1, v2);
}
if (is_conic && ALMOST_THE_SAME(fmod(v2 - v1, M_PI*2.), 0.)) {
E = BRepBuilderAPI_MakeEdge(curve).Edge();
} else {
E = BRepBuilderAPI_MakeEdge(curve, v1, v2).Edge();
}
}
if (reversed) {
E.Reverse();
}
} else {
if (e.start.which() != 0) {
throw std::runtime_error("Non-cartesian trim on edge without curve");
}
auto p1 = OpenCascadeKernel::convert_xyz<gp_Pnt>(boost::get<taxonomy::point3>(e.start));
auto p2 = OpenCascadeKernel::convert_xyz<gp_Pnt>(boost::get<taxonomy::point3>(e.end));
E = BRepBuilderAPI_MakeEdge(p1, p2).Edge();
}
BRep_Builder B;
TopoDS_Wire W;
B.MakeWire(W);
B.Add(W, E);
return result = W;
}
curve_creation_visitor_result_type operator()(const taxonomy::offset_curve& l) {
// @todo
throw std::runtime_error("Offset curves not supported as part of loop");
}
};
curve_creation_visitor_result_type convert_curve(OpenCascadeKernel* kernel, const taxonomy::item* curve) {
curve_creation_visitor v{ kernel };
if (dispatch_curve_creation<curve_creation_visitor, 0>::dispatch(curve, v)) {
return v.result;
} else {
throw std::runtime_error("No curve created");
}
}
}
bool OpenCascadeKernel::convert(const taxonomy::loop* loop, TopoDS_Wire& wire) {
auto segments = loop->children_as<taxonomy::edge>();
TopTools_ListOfShape converted_segments;
for (auto& segment : segments) {
auto segment_wire = boost::get<TopoDS_Wire>(convert_curve(this, segment));
#ifdef IFOPSH_DEBUG
std::ostringstream o;
segment->print(o);
TopoDS_Vertex v0, v1;
TopExp::Vertices(segment_wire, v0, v1);
gp_Pnt p0 = BRep_Tool::Pnt(v0);
gp_Pnt p1 = BRep_Tool::Pnt(v1);
o << "p0 " << p0.X() << " " << p0.Y() << " " << p0.Z() << std::endl;
o << "p1 " << p1.X() << " " << p1.Y() << " " << p1.Z() << std::endl;
auto o_str = o.str();
std::wcout << o_str.c_str() << std::endl;
#endif
if (!segment->orientation_2.get_value_or(true)) {
segment_wire.Reverse();
}
ShapeFix_ShapeTolerance FTol;
FTol.SetTolerance(segment_wire, precision_, TopAbs_WIRE);
converted_segments.Append(segment_wire);
}
if (converted_segments.Extent() == 0) {
Logger::Message(Logger::LOG_ERROR, "No segment succesfully converted:", loop->instance);
return false;
}
BRepBuilderAPI_MakeWire w;
TopoDS_Vertex wire_first_vertex, wire_last_vertex, edge_first_vertex, edge_last_vertex;
TopTools_ListIteratorOfListOfShape it(converted_segments);
/*
@todo
IfcEntityList::ptr profile = l->data().getInverse(&IfcSchema::IfcProfileDef::Class(), -1);
const bool force_close = profile && profile->size() > 0;
*/
const bool force_close = false;
wire_builder bld(precision_, loop->instance->as<IfcUtil::IfcBaseEntity>());
shape_pair_enumerate(it, bld, force_close);
wire = bld.wire();
return true;
}
@@ -0,0 +1,46 @@
#include "OpenCascadeKernel.h"
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
using namespace IfcGeom;
using namespace IfcGeom::util;
bool OpenCascadeKernel::convert(const taxonomy::matrix4* matrix, gp_GTrsf& trsf) {
// @todo check
const auto& m = matrix->ccomponents();
gp_Mat mat(
m(0, 0), m(0, 1), m(0, 2),
m(1, 0), m(1, 1), m(1, 2),
m(2, 0), m(2, 1), m(2, 2)
);
if (matrix->instance && matrix->instance->declaration().name() == "IfcCartesianTransformationOperator3DnonUniform") {
std::wcout << "non uniform" << std::endl;
}
// @nb SetVectorialPart() sets gp_GTrsf.scale to 0.0, causing an non-invertable
// matrix later on which cannot be in TopLoc_Location.
std::array<double, 3> ms{ {
mat.Column(1).Modulus(),
mat.Column(2).Modulus(),
mat.Column(3).Modulus()
} };
std::sort(ms.begin(), ms.end());
if (std::fabs(ms.front() - ms.back()) < 1.e-7) {
gp_Trsf tr;
tr.SetValues(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
m(2, 0), m(2, 1), m(2, 2), m(2, 3)
);
trsf = tr;
} else {
trsf.SetVectorialPart(mat);
trsf.SetTranslationPart(gp_XYZ(m(0, 3), m(1, 3), m(2, 3)));
trsf.SetForm();
}
return true;
}
+103
View File
@@ -0,0 +1,103 @@
#include "OpenCascadeKernel.h"
#include "base_utils.h"
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
using namespace IfcGeom;
using namespace IfcGeom::util;
bool OpenCascadeKernel::convert(const taxonomy::shell* l, TopoDS_Shape& shape) {
std::unique_ptr<faceset_helper> helper_scope;
helper_scope.reset(new faceset_helper(this, l));
faceset_helper_ = helper_scope.get();
auto faces = l->children_as<taxonomy::face>();
double minimal_face_area = precision_ * precision_ * 0.5;
double min_face_area = faceset_helper_
? (faceset_helper_->epsilon() * faceset_helper_->epsilon() / 20.)
: minimal_face_area;
TopTools_ListOfShape face_list;
for (auto& face : faces) {
bool success = false;
TopoDS_Face occ_face;
try {
success = convert(face, occ_face);
} catch (const std::exception& e) {
Logger::Error(e);
} catch (const Standard_Failure& e) {
if (e.GetMessageString() && strlen(e.GetMessageString())) {
Logger::Error(e.GetMessageString());
} else {
Logger::Error("Unknown error creating face");
}
} catch (...) {
Logger::Error("Unknown error creating face");
}
if (!success) {
Logger::Message(Logger::LOG_WARNING, "Failed to convert face:", face->instance);
continue;
}
if (occ_face.ShapeType() == TopAbs_COMPOUND) {
TopoDS_Iterator face_it(occ_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) > min_face_area) {
face_list.Append(triangle);
} else {
Logger::Message(Logger::LOG_WARNING, "Degenerate face:", face->instance);
}
}
}
} else {
if (face_area(occ_face) > min_face_area) {
face_list.Append(occ_face);
} else {
Logger::Message(Logger::LOG_WARNING, "Degenerate face:", face->instance);
}
}
}
if (face_list.Extent() == 0) {
return false;
}
// @todo
/* face_list.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || */
if (!create_solid_from_faces(face_list, shape, conv_settings_.getValue(ifcopenshell::geometry::ConversionSettings::GV_PRECISION))) {
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());
}
shape = compound;
}
return true;
}
bool OpenCascadeKernel::convert_impl(const taxonomy::shell *shell, IfcGeom::ConversionResults& results) {
TopoDS_Shape shape;
if (!convert(shell, shape)) {
return false;
}
results.emplace_back(ConversionResult(
shell->instance->data().id(),
shell->matrix,
new OpenCascadeShape(shape),
shell->surface_style
));
return true;
}
+96
View File
@@ -0,0 +1,96 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include "OpenCascadeKernel.h"
#include <BRepPrimAPI_MakeHalfSpace.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepPrimAPI_MakePrism.hxx>
#include <BRepAlgoAPI_Common.hxx>
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
using namespace IfcGeom;
using namespace IfcGeom::util;
bool OpenCascadeKernel::convert(const taxonomy::solid* solid, TopoDS_Shape& result) {
BRep_Builder BB;
TopoDS_Solid S;
for (auto& s : solid->children) {
if (s->kind() == taxonomy::FACE) {
// halfspace
if (solid->children.size() != 1) {
throw std::runtime_error("Unexpected number of children on solid");
}
auto face = (taxonomy::face*) s;
const auto& m = ((taxonomy::geom_item*)face->basis)->matrix.ccomponents();
gp_Pln pln(convert_xyz2<gp_Pnt>(m.col(3)), convert_xyz2<gp_Dir>(m.col(2)));
const gp_Pnt pnt = pln.Location().Translated(face->orientation.get_value_or(false) ? pln.Axis().Direction() : -pln.Axis().Direction());
TopoDS_Shape halfspace = BRepPrimAPI_MakeHalfSpace(BRepBuilderAPI_MakeFace(pln), pnt).Solid();
if (!face->children.empty()) {
TopoDS_Wire wire;
gp_GTrsf gtrsf;
if (convert((taxonomy::loop*)face->children[0], wire) && wire.Closed() && convert(&face->matrix, gtrsf)) {
gp_Trsf trsf = gtrsf.Trsf();
TopoDS_Shape prism = BRepPrimAPI_MakePrism(BRepBuilderAPI_MakeFace(wire), gp_Vec(0, 0, 200));
gp_Trsf down; down.SetTranslation(gp_Vec(0, 0, -100.0));
// `trsf` and `down` both have a unit scale factor
prism.Move(trsf*down);
halfspace = BRepAlgoAPI_Common(halfspace, prism);
}
}
result = halfspace;
return true;
} else {
if (s->kind() != taxonomy::SHELL) {
throw std::runtime_error("Unexpected child in solid");
}
if (S.IsNull()) {
BB.MakeSolid(S);
}
TopoDS_Shell shl;
convert(((taxonomy::shell*)s), shl);
BB.Add(S, shl);
}
}
if (!S.IsNull()) {
result = S;
}
}
bool OpenCascadeKernel::convert_impl(const taxonomy::solid* solid , IfcGeom::ConversionResults& results) {
TopoDS_Shape shape;
if (!convert(solid, shape)) {
return false;
}
results.emplace_back(ConversionResult(
solid->instance->data().id(),
solid->matrix,
new OpenCascadeShape(shape),
solid->surface_style
));
return true;
}
@@ -21,9 +21,8 @@
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepOffsetAPI_MakePipeShell.hxx>
#include "../ifcparse/IfcLogger.h"
#include "../ifcgeom_schema_agnostic/Kernel.h"
#include "../ifcgeom_schema_agnostic/base_utils.h"
#include "../../../ifcparse/IfcLogger.h"
#include "base_utils.h"
bool IfcGeom::util::wire_is_c1_continuous(const TopoDS_Wire & w, double tol) {
// NB Note that c0 continuity is NOT checked!
@@ -1,7 +1,7 @@
#include "wire_builder.h"
#include "../ifcparse/IfcLogger.h"
#include "../ifcgeom_schema_agnostic/Kernel.h"
#include "../../../ifcparse/IfcLogger.h"
#include "../../../ifcgeom/ConversionSettings.h"
#include <TopExp.hxx>
#include <TopoDS.hxx>
@@ -20,7 +20,7 @@
#ifndef WIRE_BUILDER_H
#define WIRE_BUILDER_H
#include "../ifcparse/IfcBaseClass.h"
#include "../../../ifcparse/IfcBaseClass.h"
#include <Geom_Curve.hxx>
@@ -1,10 +1,11 @@
#include "wire_utils.h"
#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 "../../../ifcparse/IfcLogger.h"
#include "../../../ifcgeom/ConversionSettings.h"
#include "base_utils.h"
#include "boolean_utils.h"
#include "IfcGeomTree.h"
#include <TopExp.hxx>
#include <TopoDS.hxx>