mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-17 05:52:33 +00:00
Proceed with merge
This commit is contained in:
@@ -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_;
|
||||
};
|
||||
|
||||
+1269
-126
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user