mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 23:36:20 +00:00
triangulation-type setting for non-triangulated polyhedral from iterator
This commit is contained in:
@@ -78,3 +78,19 @@ std::istream& ifcopenshell::geometry::settings::operator>>(std::istream& in, Out
|
|||||||
}
|
}
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::istream& ifcopenshell::geometry::settings::operator>>(std::istream& in, TriangulationMethod& v) {
|
||||||
|
std::string token;
|
||||||
|
in >> token;
|
||||||
|
boost::to_upper(token);
|
||||||
|
if (token == "TRIANGLE_MESH") {
|
||||||
|
v = TRIANGLE_MESH;
|
||||||
|
} else if (token == "POLYHEDRON_WITHOUT_HOLES") {
|
||||||
|
v = POLYHEDRON_WITHOUT_HOLES;
|
||||||
|
} else if (token == "POLYHEDRON_WITH_HOLES") {
|
||||||
|
v = POLYHEDRON_WITH_HOLES;
|
||||||
|
} else {
|
||||||
|
in.setstate(std::ios_base::failbit);
|
||||||
|
}
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|||||||
@@ -378,12 +378,28 @@ namespace ifcopenshell {
|
|||||||
static constexpr const char* const name = "model-rotation";
|
static constexpr const char* const name = "model-rotation";
|
||||||
static constexpr const char* const description = "Applies an arbitrary quaternion rotation of form 'x,y,z,w' to all placements.";
|
static constexpr const char* const description = "Applies an arbitrary quaternion rotation of form 'x,y,z,w' to all placements.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum TriangulationMethod {
|
||||||
|
TRIANGLE_MESH,
|
||||||
|
POLYHEDRON_WITHOUT_HOLES,
|
||||||
|
POLYHEDRON_WITH_HOLES
|
||||||
|
};
|
||||||
|
|
||||||
|
std::istream& operator>>(std::istream& in, TriangulationMethod& ioo);
|
||||||
|
|
||||||
|
struct TriangulationType : public SettingBase<TriangulationType, TriangulationMethod> {
|
||||||
|
static constexpr const char* const name = "triangulation-type";
|
||||||
|
static constexpr const char* const description = "Type of planar facet to be emitted";
|
||||||
|
static constexpr TriangulationMethod defaultvalue = TRIANGLE_MESH;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename settings_t>
|
template <typename settings_t>
|
||||||
class IFC_GEOM_API SettingsContainer {
|
class IFC_GEOM_API SettingsContainer {
|
||||||
public:
|
public:
|
||||||
typedef boost::variant<bool, int, double, std::string, std::set<int>, std::set<std::string>, std::vector<double>, IteratorOutputOptions, PiecewiseStepMethod, OutputDimensionalityTypes> value_variant_t;
|
typedef boost::variant<bool, int, double, std::string, std::set<int>, std::set<std::string>, std::vector<double>, IteratorOutputOptions, PiecewiseStepMethod, OutputDimensionalityTypes, TriangulationMethod> value_variant_t;
|
||||||
private:
|
private:
|
||||||
settings_t settings;
|
settings_t settings;
|
||||||
|
|
||||||
@@ -470,73 +486,12 @@ namespace ifcopenshell {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class IFC_GEOM_API Settings : public SettingsContainer<
|
class IFC_GEOM_API Settings : public SettingsContainer<
|
||||||
std::tuple<MesherLinearDeflection, MesherAngularDeflection, ReorientShells, LengthUnit, PlaneUnit, Precision, OutputDimensionality, LayersetFirst, DisableBooleanResult, NoWireIntersectionCheck, NoWireIntersectionTolerance, PrecisionFactor, DebugBooleanOperations, BooleanAttempt2d, WeldVertices, UseWorldCoords, UseMaterialNames, ConvertBackUnits, ContextIds, ContextTypes, ContextIdentifiers, IteratorOutput, DisableOpeningSubtractions, ApplyDefaultMaterials, DontEmitNormals, GenerateUvs, ApplyLayerSets, UseElementHierarchy, ValidateQuantities, EdgeArrows, BuildingLocalPlacement, SiteLocalPlacement, ForceSpaceTransparency, CircleSegments, KeepBoundingBoxes, PiecewiseStepType, PiecewiseStepParam, NoParallelMapping, ModelOffset, ModelRotation>
|
std::tuple<MesherLinearDeflection, MesherAngularDeflection, ReorientShells, LengthUnit, PlaneUnit, Precision, OutputDimensionality, LayersetFirst, DisableBooleanResult, NoWireIntersectionCheck, NoWireIntersectionTolerance, PrecisionFactor, DebugBooleanOperations, BooleanAttempt2d, WeldVertices, UseWorldCoords, UseMaterialNames, ConvertBackUnits, ContextIds, ContextTypes, ContextIdentifiers, IteratorOutput, DisableOpeningSubtractions, ApplyDefaultMaterials, DontEmitNormals, GenerateUvs, ApplyLayerSets, UseElementHierarchy, ValidateQuantities, EdgeArrows, BuildingLocalPlacement, SiteLocalPlacement, ForceSpaceTransparency, CircleSegments, KeepBoundingBoxes, PiecewiseStepType, PiecewiseStepParam, NoParallelMapping, ModelOffset, ModelRotation, TriangulationType>
|
||||||
>
|
>
|
||||||
{};
|
{};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// namespace ifcopenshell {
|
|
||||||
// namespace geometry {
|
|
||||||
//
|
|
||||||
// class IFC_GEOM_API ConversionSettings {
|
|
||||||
// public:
|
|
||||||
// // Tolerances and settings for various geometrical operations:
|
|
||||||
// enum GeomValue {
|
|
||||||
// //
|
|
||||||
// // Default: 0.001m / 1mm
|
|
||||||
// GV_DEFLECTION_TOLERANCE,
|
|
||||||
//
|
|
||||||
// // The length unit used the creation of TopoDS_Shapes, primarily affects the
|
|
||||||
// // interpretation of IfcCartesianPoints and IfcVector magnitudes
|
|
||||||
// // DefaultL 1.0
|
|
||||||
// GV_LENGTH_UNIT,
|
|
||||||
// // The plane angle unit used for the creation of TopoDS_Shapes, primarily affects
|
|
||||||
// // the interpretation of IfcParamaterValues of IfcTrimmedCurves
|
|
||||||
// // Default: -1.0 (= not set, fist try degrees, then radians)
|
|
||||||
// GV_PLANEANGLE_UNIT,
|
|
||||||
// // The precision used in boolean operations, setting this value too low results
|
|
||||||
// // in artefacts and potentially modelling failures
|
|
||||||
// // Default: 0.00001 (obtained from IfcGeometricRepresentationContext if available)
|
|
||||||
// GV_PRECISION,
|
|
||||||
// // Whether to process shapes of type Face or higher (1) Wire or lower (-1) or all (0)
|
|
||||||
// GV_DIMENSIONALITY,
|
|
||||||
// GV_LAYERSET_FIRST,
|
|
||||||
// GV_DISABLE_BOOLEAN_RESULT,
|
|
||||||
// GV_NO_WIRE_INTERSECTION_CHECK,
|
|
||||||
// GV_PRECISION_FACTOR,
|
|
||||||
// GV_NO_WIRE_INTERSECTION_TOLERANCE,
|
|
||||||
// GV_DEBUG_BOOLEAN,
|
|
||||||
// GV_BOOLEAN_ATTEMPT_2D,
|
|
||||||
// NUM_SETTINGS
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// void setValue(GeomValue var, double value);
|
|
||||||
//
|
|
||||||
// double getValue(GeomValue var) const;
|
|
||||||
//
|
|
||||||
// private:
|
|
||||||
// std::array<double, NUM_SETTINGS> values_ = {
|
|
||||||
// /* deflection_tolerance = */ 0.001,
|
|
||||||
// // @todo make sure these 'read-only' variables work.
|
|
||||||
// /* minimal_face_area = */ std::numeric_limits<double>::quiet_NaN(),
|
|
||||||
// /* 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.,
|
|
||||||
// /* disable_boolean_result = */ -1.
|
|
||||||
// /* no_wire_intersection_check = */ -1.,
|
|
||||||
// /* precision_factor = */ 10.,
|
|
||||||
// /* no_wire_intersection_tolerance = */ -1.,
|
|
||||||
// /* boolean_debug_setting = */ -1.,
|
|
||||||
// /* boolean_attempt_2d = */ 1.
|
|
||||||
// };
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @todo find a place
|
// @todo find a place
|
||||||
namespace IfcGeom {
|
namespace IfcGeom {
|
||||||
class IFC_GEOM_API geometry_exception : public std::exception {
|
class IFC_GEOM_API geometry_exception : public std::exception {
|
||||||
|
|||||||
@@ -332,23 +332,23 @@ IfcGeom::Representation::Triangulation::Triangulation(const BRep& shape_model)
|
|||||||
|
|
||||||
int surface_style_id = -1;
|
int surface_style_id = -1;
|
||||||
if (iit->hasStyle()) {
|
if (iit->hasStyle()) {
|
||||||
auto jt = std::find(_materials.begin(), _materials.end(), iit->StylePtr());
|
auto jt = std::find(materials_.begin(), materials_.end(), iit->StylePtr());
|
||||||
if (jt == _materials.end()) {
|
if (jt == materials_.end()) {
|
||||||
surface_style_id = (int)_materials.size();
|
surface_style_id = (int)materials_.size();
|
||||||
_materials.push_back(iit->StylePtr());
|
materials_.push_back(iit->StylePtr());
|
||||||
} else {
|
} else {
|
||||||
surface_style_id = (int)(jt - _materials.begin());
|
surface_style_id = (int)(jt - materials_.begin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings().get<ifcopenshell::geometry::settings::ApplyDefaultMaterials>().get() && surface_style_id == -1) {
|
if (settings().get<ifcopenshell::geometry::settings::ApplyDefaultMaterials>().get() && surface_style_id == -1) {
|
||||||
const auto& material = IfcGeom::get_default_style(shape_model.entity());
|
const auto& material = IfcGeom::get_default_style(shape_model.entity());
|
||||||
auto mit = std::find(_materials.begin(), _materials.end(), material);
|
auto mit = std::find(materials_.begin(), materials_.end(), material);
|
||||||
if (mit == _materials.end()) {
|
if (mit == materials_.end()) {
|
||||||
surface_style_id = (int)_materials.size();
|
surface_style_id = (int)materials_.size();
|
||||||
_materials.push_back(material);
|
materials_.push_back(material);
|
||||||
} else {
|
} else {
|
||||||
surface_style_id = (int)(mit - _materials.begin());
|
surface_style_id = (int)(mit - materials_.begin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,7 +393,7 @@ int IfcGeom::Representation::Triangulation::addVertex(int item_id, int material_
|
|||||||
const double X = convert ? (pX /unit_magnitude) : pX;
|
const double X = convert ? (pX /unit_magnitude) : pX;
|
||||||
const double Y = convert ? (pY /unit_magnitude) : pY;
|
const double Y = convert ? (pY /unit_magnitude) : pY;
|
||||||
const double Z = convert ? (pZ /unit_magnitude) : pZ;
|
const double Z = convert ? (pZ /unit_magnitude) : pZ;
|
||||||
int i = (int)_verts.size() / 3;
|
int i = (int)verts_.size() / 3;
|
||||||
if (settings().get<ifcopenshell::geometry::settings::WeldVertices>().get()) {
|
if (settings().get<ifcopenshell::geometry::settings::WeldVertices>().get()) {
|
||||||
const VertexKey key = std::make_tuple(item_id, material_index, X, Y, Z);
|
const VertexKey key = std::make_tuple(item_id, material_index, X, Y, Z);
|
||||||
typename VertexKeyMap::const_iterator it = welds.find(key);
|
typename VertexKeyMap::const_iterator it = welds.find(key);
|
||||||
@@ -401,9 +401,9 @@ int IfcGeom::Representation::Triangulation::addVertex(int item_id, int material_
|
|||||||
i = (int)(welds.size() + weld_offset_);
|
i = (int)(welds.size() + weld_offset_);
|
||||||
welds[key] = i;
|
welds[key] = i;
|
||||||
}
|
}
|
||||||
_verts.push_back(X);
|
verts_.push_back(X);
|
||||||
_verts.push_back(Y);
|
verts_.push_back(Y);
|
||||||
_verts.push_back(Z);
|
verts_.push_back(Z);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,14 +103,20 @@ namespace IfcGeom {
|
|||||||
typedef std::map<VertexKey, int> VertexKeyMap;
|
typedef std::map<VertexKey, int> VertexKeyMap;
|
||||||
typedef std::pair<int, int> Edge;
|
typedef std::pair<int, int> Edge;
|
||||||
|
|
||||||
std::vector<double> _verts;
|
std::vector<double> verts_;
|
||||||
std::vector<int> _faces;
|
|
||||||
std::vector<int> _edges;
|
// @nb only one of these is populated based on settings, we didn't want to go
|
||||||
std::vector<double> _normals;
|
// all in with templates or subtypes because of reduced ease of use.
|
||||||
|
std::vector<int> faces_;
|
||||||
|
std::vector<std::vector<int>> polyhedral_faces_without_holes_;
|
||||||
|
std::vector<std::vector<std::vector<int>>> polyhedral_faces_with_holes_;
|
||||||
|
|
||||||
|
std::vector<int> edges_;
|
||||||
|
std::vector<double> normals_;
|
||||||
std::vector<double> uvs_;
|
std::vector<double> uvs_;
|
||||||
std::vector<int> _material_ids;
|
std::vector<int> material_ids_;
|
||||||
std::vector<ifcopenshell::geometry::taxonomy::style::ptr> _materials;
|
std::vector<ifcopenshell::geometry::taxonomy::style::ptr> materials_;
|
||||||
std::vector<int> _item_ids;
|
std::vector<int> item_ids_;
|
||||||
size_t weld_offset_;
|
size_t weld_offset_;
|
||||||
VertexKeyMap welds;
|
VertexKeyMap welds;
|
||||||
|
|
||||||
@@ -120,15 +126,17 @@ namespace IfcGeom {
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const std::vector<double>& verts() const { return _verts; }
|
const std::vector<double>& verts() const { return verts_; }
|
||||||
const std::vector<int>& faces() const { return _faces; }
|
const std::vector<int>& faces() const { return faces_; }
|
||||||
const std::vector<int>& edges() const { return _edges; }
|
const std::vector<std::vector<int>>& polyhedral_faces_without_holes() const { return polyhedral_faces_without_holes_; }
|
||||||
const std::vector<double>& normals() const { return _normals; }
|
const std::vector<std::vector<std::vector<int>>>& polyhedral_faces_with_holes() const { return polyhedral_faces_with_holes_; }
|
||||||
|
const std::vector<int>& edges() const { return edges_; }
|
||||||
|
const std::vector<double>& normals() const { return normals_; }
|
||||||
std::vector<double>& uvs() { return uvs_; }
|
std::vector<double>& uvs() { return uvs_; }
|
||||||
const std::vector<double>& uvs() const { return uvs_; }
|
const std::vector<double>& uvs() const { return uvs_; }
|
||||||
const std::vector<int>& material_ids() const { return _material_ids; }
|
const std::vector<int>& material_ids() const { return material_ids_; }
|
||||||
const std::vector<ifcopenshell::geometry::taxonomy::style::ptr>& materials() const { return _materials; }
|
const std::vector<ifcopenshell::geometry::taxonomy::style::ptr>& materials() const { return materials_; }
|
||||||
const std::vector<int>& item_ids() const { return _item_ids; }
|
const std::vector<int>& item_ids() const { return item_ids_; }
|
||||||
|
|
||||||
Triangulation(const BRep& shape_model);
|
Triangulation(const BRep& shape_model);
|
||||||
|
|
||||||
@@ -146,14 +154,14 @@ namespace IfcGeom {
|
|||||||
const std::vector<int>& item_ids
|
const std::vector<int>& item_ids
|
||||||
)
|
)
|
||||||
: Representation(settings, entity, id)
|
: Representation(settings, entity, id)
|
||||||
, _verts(verts)
|
, verts_(verts)
|
||||||
, _faces(faces)
|
, faces_(faces)
|
||||||
, _edges(edges)
|
, edges_(edges)
|
||||||
, _normals(normals)
|
, normals_(normals)
|
||||||
, uvs_(uvs)
|
, uvs_(uvs)
|
||||||
, _material_ids(material_ids)
|
, material_ids_(material_ids)
|
||||||
, _materials(materials)
|
, materials_(materials)
|
||||||
, _item_ids(item_ids)
|
, item_ids_(item_ids)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual ~Triangulation() {}
|
virtual ~Triangulation() {}
|
||||||
@@ -168,30 +176,44 @@ namespace IfcGeom {
|
|||||||
int addVertex(int item_index, int material_index, double X, double Y, double Z);
|
int addVertex(int item_index, int material_index, double X, double Y, double Z);
|
||||||
|
|
||||||
void addNormal(double X, double Y, double Z) {
|
void addNormal(double X, double Y, double Z) {
|
||||||
_normals.push_back(X);
|
normals_.push_back(X);
|
||||||
_normals.push_back(Y);
|
normals_.push_back(Y);
|
||||||
_normals.push_back(Z);
|
normals_.push_back(Z);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addFace(int item_id, int style, int i0, int i1, int i2) {
|
void addFace(int item_id, int style, int i0, int i1, int i2) {
|
||||||
_faces.push_back(i0);
|
faces_.push_back(i0);
|
||||||
_faces.push_back(i1);
|
faces_.push_back(i1);
|
||||||
_faces.push_back(i2);
|
faces_.push_back(i2);
|
||||||
|
|
||||||
_item_ids.push_back(item_id);
|
item_ids_.push_back(item_id);
|
||||||
_material_ids.push_back(style);
|
material_ids_.push_back(style);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addFace(int item_id, int style, const std::vector<int>& outer_bound) {
|
||||||
|
polyhedral_faces_without_holes_.push_back(outer_bound);
|
||||||
|
|
||||||
|
item_ids_.push_back(item_id);
|
||||||
|
material_ids_.push_back(style);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addFace(int item_id, int style, const std::vector<std::vector<int>>& bounds) {
|
||||||
|
polyhedral_faces_with_holes_.push_back(bounds);
|
||||||
|
|
||||||
|
item_ids_.push_back(item_id);
|
||||||
|
material_ids_.push_back(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addEdge(int style, int i0, int i1) {
|
void addEdge(int style, int i0, int i1) {
|
||||||
_edges.push_back(i0);
|
edges_.push_back(i0);
|
||||||
_edges.push_back(i1);
|
edges_.push_back(i1);
|
||||||
|
|
||||||
_material_ids.push_back(style);
|
material_ids_.push_back(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerEdge(int i0, int i1) {
|
void registerEdge(int i0, int i1) {
|
||||||
_edges.push_back(i0);
|
edges_.push_back(i0);
|
||||||
_edges.push_back(i1);
|
edges_.push_back(i1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addEdge(int n1, int n2, std::map<std::pair<int, int>, int>& edgecount);
|
void addEdge(int n1, int n2, std::map<std::pair<int, int>, int>& edgecount);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <BRepGProp.hxx>
|
#include <BRepGProp.hxx>
|
||||||
#include <GProp_GProps.hxx>
|
#include <GProp_GProps.hxx>
|
||||||
#include <Geom_SphericalSurface.hxx>
|
#include <Geom_SphericalSurface.hxx>
|
||||||
|
#include <Geom_Plane.hxx>
|
||||||
|
|
||||||
#include "OpenCascadeConversionResult.h"
|
#include "OpenCascadeConversionResult.h"
|
||||||
|
|
||||||
@@ -15,6 +16,12 @@
|
|||||||
|
|
||||||
#include <Standard_Version.hxx>
|
#include <Standard_Version.hxx>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <tuple>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#if OCC_VERSION_HEX >= 0x70600
|
#if OCC_VERSION_HEX >= 0x70600
|
||||||
#include <TopTools_FormatVersion.hxx>
|
#include <TopTools_FormatVersion.hxx>
|
||||||
#endif
|
#endif
|
||||||
@@ -24,6 +31,44 @@ using IfcGeom::OpaqueCoordinate;
|
|||||||
using IfcGeom::NumberNativeDouble;
|
using IfcGeom::NumberNativeDouble;
|
||||||
using IfcGeom::ConversionResultShape;
|
using IfcGeom::ConversionResultShape;
|
||||||
|
|
||||||
|
struct EdgeKey {
|
||||||
|
int v1, v2;
|
||||||
|
|
||||||
|
// These are not part of the hash or equality,
|
||||||
|
// but retained to easily created a directed
|
||||||
|
// graph of the original boundary edges. Since
|
||||||
|
// the boundary edges are exactly those with
|
||||||
|
// count=1 we don't need to worry about
|
||||||
|
// conflicting original vertex indices.
|
||||||
|
int ov1, ov2;
|
||||||
|
|
||||||
|
EdgeKey(int a, int b)
|
||||||
|
: ov1(a)
|
||||||
|
, ov2(b)
|
||||||
|
{
|
||||||
|
if (a < b) {
|
||||||
|
v1 = a;
|
||||||
|
v2 = b;
|
||||||
|
} else {
|
||||||
|
v1 = b;
|
||||||
|
v2 = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const EdgeKey& other) const {
|
||||||
|
return v1 == other.v1 && v2 == other.v2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<EdgeKey> {
|
||||||
|
std::size_t operator()(const EdgeKey& ek) const {
|
||||||
|
return std::hash<int>()(ek.v1) ^ std::hash<int>()(ek.v2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// We bypass the conversion to gp_GTrsf, because it does not work
|
// We bypass the conversion to gp_GTrsf, because it does not work
|
||||||
void taxonomy_transform(const Eigen::Matrix4d* m, gp_XYZ& xyz) {
|
void taxonomy_transform(const Eigen::Matrix4d* m, gp_XYZ& xyz) {
|
||||||
@@ -35,6 +80,78 @@ namespace {
|
|||||||
xyz.ChangeData()[2] = v2(2);
|
xyz.ChangeData()[2] = v2(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to find boundary loops from triangles
|
||||||
|
std::vector<std::vector<int>> find_boundary_loops(const std::vector<double>& positions, const std::vector<std::tuple<int, int, int>>& triangles) {
|
||||||
|
std::unordered_map<EdgeKey, int> edge_count;
|
||||||
|
|
||||||
|
// Count how many triangles each edge belongs to
|
||||||
|
for (const auto& triangle : triangles) {
|
||||||
|
int v1, v2, v3;
|
||||||
|
std::tie(v1, v2, v3) = triangle;
|
||||||
|
|
||||||
|
edge_count[{v1, v2}]++;
|
||||||
|
edge_count[{v2, v3}]++;
|
||||||
|
edge_count[{v3, v1}]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boundary edges have count 1
|
||||||
|
std::vector<EdgeKey> boundary_edges;
|
||||||
|
for (auto& p : edge_count) {
|
||||||
|
if (p.second == 1) {
|
||||||
|
boundary_edges.push_back(p.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We retained original directed edges so we build
|
||||||
|
// a mapping out of these directed edges.
|
||||||
|
std::unordered_map<int, int> vertex_successors;
|
||||||
|
for (const auto& e : boundary_edges) {
|
||||||
|
vertex_successors[e.ov1] = e.ov2;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> loops;
|
||||||
|
while (!vertex_successors.empty()) {
|
||||||
|
loops.emplace_back();
|
||||||
|
auto it = vertex_successors.begin();
|
||||||
|
loops.back() = { it->first, it->second };
|
||||||
|
vertex_successors.erase(it);
|
||||||
|
|
||||||
|
int current = loops.back().back();
|
||||||
|
while (!vertex_successors.empty() && current != loops.back().front()) {
|
||||||
|
auto next = vertex_successors[current];
|
||||||
|
if (loops.back().front() != next) {
|
||||||
|
loops.back().push_back(next);
|
||||||
|
}
|
||||||
|
vertex_successors.erase(current);
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the loops by smallest x-coord of their constituent positions
|
||||||
|
// In order to put the outermost loop in front
|
||||||
|
if (loops.size() > 1) {
|
||||||
|
std::vector<std::pair<double, size_t>> min_xs;
|
||||||
|
for (auto& l : loops) {
|
||||||
|
double min_x = std::numeric_limits<double>::infinity();
|
||||||
|
for (auto& i : l) {
|
||||||
|
const auto& x = positions[i * 3];
|
||||||
|
if (x < min_x) {
|
||||||
|
min_x = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
min_xs.push_back({ min_x, min_xs.size() });
|
||||||
|
}
|
||||||
|
std::sort(min_xs.begin(), min_xs.end());
|
||||||
|
decltype(loops) loops_copy;
|
||||||
|
for (auto& p : min_xs) {
|
||||||
|
loops_copy.emplace_back(std::move(loops[p.second]));
|
||||||
|
}
|
||||||
|
std::swap(loops, loops_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return loops;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometry::Settings settings, const ifcopenshell::geometry::taxonomy::matrix4& place, IfcGeom::Representation::Triangulation* t, int item_id, int surface_style_id) const {
|
void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometry::Settings settings, const ifcopenshell::geometry::taxonomy::matrix4& place, IfcGeom::Representation::Triangulation* t, int item_id, int surface_style_id) const {
|
||||||
@@ -71,6 +188,18 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometr
|
|||||||
TopExp_Explorer exp;
|
TopExp_Explorer exp;
|
||||||
for (exp.Init(shape_, TopAbs_FACE); exp.More(); exp.Next(), ++num_faces) {
|
for (exp.Init(shape_, TopAbs_FACE); exp.More(); exp.Next(), ++num_faces) {
|
||||||
TopoDS_Face face = TopoDS::Face(exp.Current());
|
TopoDS_Face face = TopoDS::Face(exp.Current());
|
||||||
|
|
||||||
|
size_t num_bounds = 0;
|
||||||
|
for (TopoDS_Iterator it(face); it.More(); it.Next(), ++num_bounds) {}
|
||||||
|
|
||||||
|
const bool is_planar = BRep_Tool::Surface(face) && BRep_Tool::Surface(face)->DynamicType() == STANDARD_TYPE(Geom_Plane);
|
||||||
|
const bool has_inner_bounds = num_bounds > 1;
|
||||||
|
|
||||||
|
const bool polyhedral_output_with_holes = settings.get<settings::TriangulationType>().get() == settings::POLYHEDRON_WITH_HOLES && is_planar;
|
||||||
|
const bool polyhedral_output_without_holes = settings.get<settings::TriangulationType>().get() == settings::POLYHEDRON_WITHOUT_HOLES && is_planar && !has_inner_bounds;
|
||||||
|
|
||||||
|
std::vector<std::tuple<int, int, int>> triangle_indices;
|
||||||
|
|
||||||
TopLoc_Location loc;
|
TopLoc_Location loc;
|
||||||
Handle_Poly_Triangulation tri = BRep_Tool::Triangulation(face, loc);
|
Handle_Poly_Triangulation tri = BRep_Tool::Triangulation(face, loc);
|
||||||
|
|
||||||
@@ -154,11 +283,21 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometr
|
|||||||
_normals.push_back((float)normal.Z());
|
_normals.push_back((float)normal.Z());
|
||||||
*/
|
*/
|
||||||
|
|
||||||
t->addFace(item_id, surface_style_id, dict[n1], dict[n2], dict[n3]);
|
if (polyhedral_output_without_holes || polyhedral_output_with_holes) {
|
||||||
|
triangle_indices.push_back({ dict[n1], dict[n2], dict[n3] });
|
||||||
|
} else {
|
||||||
|
if (settings.get<settings::TriangulationType>().get() == settings::POLYHEDRON_WITHOUT_HOLES) {
|
||||||
|
t->addFace(item_id, surface_style_id, std::vector<int>{ dict[n1], dict[n2], dict[n3] });
|
||||||
|
} else if (settings.get<settings::TriangulationType>().get() == settings::POLYHEDRON_WITH_HOLES) {
|
||||||
|
t->addFace(item_id, surface_style_id, std::vector<std::vector<int>>{{ dict[n1], dict[n2], dict[n3] }});
|
||||||
|
} else {
|
||||||
|
t->addFace(item_id, surface_style_id, dict[n1], dict[n2], dict[n3]);
|
||||||
|
|
||||||
t->addEdge(dict[n1], dict[n2], edgecount);
|
t->addEdge(dict[n1], dict[n2], edgecount);
|
||||||
t->addEdge(dict[n2], dict[n3], edgecount);
|
t->addEdge(dict[n2], dict[n3], edgecount);
|
||||||
t->addEdge(dict[n3], dict[n1], edgecount);
|
t->addEdge(dict[n3], dict[n1], edgecount);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (auto& p : edgecount) {
|
for (auto& p : edgecount) {
|
||||||
// @todo should be != 2?
|
// @todo should be != 2?
|
||||||
@@ -172,6 +311,19 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (polyhedral_output_without_holes || polyhedral_output_with_holes) {
|
||||||
|
auto loops = find_boundary_loops(t->verts(), triangle_indices);
|
||||||
|
if (polyhedral_output_without_holes) {
|
||||||
|
if (!loops.empty() && !loops[0].empty()) {
|
||||||
|
t->addFace(item_id, surface_style_id, loops[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!loops.empty()) {
|
||||||
|
t->addFace(item_id, surface_style_id, loops);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!t->normals().empty() && settings.get<settings::GenerateUvs>().get()) {
|
if (!t->normals().empty() && settings.get<settings::GenerateUvs>().get()) {
|
||||||
|
|||||||
@@ -101,6 +101,9 @@ SETTING = Literal[
|
|||||||
"piecewise-step-param",
|
"piecewise-step-param",
|
||||||
"use-python-opencascade",
|
"use-python-opencascade",
|
||||||
"no-parallel-mapping",
|
"no-parallel-mapping",
|
||||||
|
"triangulation-type",
|
||||||
|
"model-rotation",
|
||||||
|
"model-offset",
|
||||||
]
|
]
|
||||||
SERIALIZER_SETTING = Literal[
|
SERIALIZER_SETTING = Literal[
|
||||||
"use-element-names",
|
"use-element-names",
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ if os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) == sys.path[0]
|
|||||||
# does not contain the built binary
|
# does not contain the built binary
|
||||||
sys.path[0:1] = []
|
sys.path[0:1] = []
|
||||||
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api.project
|
import ifcopenshell.api.project
|
||||||
@@ -75,3 +76,8 @@ class IFC2X3:
|
|||||||
|
|
||||||
ifcopenshell.api.pre_listeners = {}
|
ifcopenshell.api.pre_listeners = {}
|
||||||
ifcopenshell.api.post_listeners = {}
|
ifcopenshell.api.post_listeners = {}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def file(request):
|
||||||
|
return ifcopenshell.open(os.path.join("test/fixtures", request.param))
|
||||||
|
|||||||
@@ -681,7 +681,17 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
|
|||||||
|
|
||||||
%pythoncode %{
|
%pythoncode %{
|
||||||
# Hide the getters with read-only property implementations
|
# Hide the getters with read-only property implementations
|
||||||
faces = property(faces)
|
faces_tri = property(faces)
|
||||||
|
polyhedral_faces_without_holes = property(polyhedral_faces_without_holes)
|
||||||
|
polyhedral_faces_with_holes = property(polyhedral_faces_with_holes)
|
||||||
|
def get_faces(self):
|
||||||
|
if self.faces_tri:
|
||||||
|
return self.faces_tri
|
||||||
|
elif self.polyhedral_faces_without_holes:
|
||||||
|
return self.polyhedral_faces_without_holes
|
||||||
|
else:
|
||||||
|
return self.polyhedral_faces_with_holes
|
||||||
|
faces = property(get_faces)
|
||||||
edges = property(edges)
|
edges = property(edges)
|
||||||
material_ids = property(material_ids)
|
material_ids = property(material_ids)
|
||||||
materials = property(materials)
|
materials = property(materials)
|
||||||
|
|||||||
@@ -56,24 +56,6 @@ private:
|
|||||||
%rename("add") addEntity;
|
%rename("add") addEntity;
|
||||||
%rename("remove") removeEntity;
|
%rename("remove") removeEntity;
|
||||||
|
|
||||||
%{
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct is_std_vector : std::false_type {};
|
|
||||||
template<typename T, typename Alloc>
|
|
||||||
struct is_std_vector<std::vector<T, Alloc>> : std::true_type {};
|
|
||||||
template<typename T>
|
|
||||||
constexpr bool is_std_vector_v = is_std_vector<T>::value;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct is_std_vector_vector : std::false_type {};
|
|
||||||
template<typename T, typename Alloc, typename Alloc2>
|
|
||||||
struct is_std_vector_vector<std::vector<std::vector<T, Alloc>, Alloc2>> : std::true_type {};
|
|
||||||
template<typename T>
|
|
||||||
constexpr bool is_std_vector_vector_v = is_std_vector_vector<T>::value;
|
|
||||||
|
|
||||||
%}
|
|
||||||
|
|
||||||
class attribute_value_derived {};
|
class attribute_value_derived {};
|
||||||
%{
|
%{
|
||||||
class attribute_value_derived {};
|
class attribute_value_derived {};
|
||||||
@@ -780,9 +762,7 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas
|
|||||||
PyObject* convert_cpp_attribute_to_python(AttributeValue arg) {
|
PyObject* convert_cpp_attribute_to_python(AttributeValue arg) {
|
||||||
return arg.array_->apply_visitor([](auto& v){
|
return arg.array_->apply_visitor([](auto& v){
|
||||||
using U = std::decay_t<decltype(v)>;
|
using U = std::decay_t<decltype(v)>;
|
||||||
if constexpr (is_std_vector_vector_v<U>) {
|
if constexpr (is_std_vector_v<U>) {
|
||||||
return pythonize_vector2(v);
|
|
||||||
} else if constexpr (is_std_vector_v<U>) {
|
|
||||||
return pythonize_vector(v);
|
return pythonize_vector(v);
|
||||||
} else if constexpr (std::is_same_v<U, EnumerationReference>) {
|
} else if constexpr (std::is_same_v<U, EnumerationReference>) {
|
||||||
return pythonize(std::string(v.value()));
|
return pythonize(std::string(v.value()));
|
||||||
|
|||||||
@@ -148,6 +148,24 @@
|
|||||||
#endif
|
#endif
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
%{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct is_std_vector : std::false_type {};
|
||||||
|
template<typename T, typename Alloc>
|
||||||
|
struct is_std_vector<std::vector<T, Alloc>> : std::true_type {};
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool is_std_vector_v = is_std_vector<T>::value;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct is_std_vector_vector : std::false_type {};
|
||||||
|
template<typename T, typename Alloc, typename Alloc2>
|
||||||
|
struct is_std_vector_vector<std::vector<std::vector<T, Alloc>, Alloc2>> : std::true_type {};
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool is_std_vector_vector_v = is_std_vector_vector<T>::value;
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
// Create docstrings for generated python code.
|
// Create docstrings for generated python code.
|
||||||
%feature("autodoc", "1");
|
%feature("autodoc", "1");
|
||||||
|
|
||||||
|
|||||||
@@ -189,21 +189,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
PyObject* pythonize_vector(const std::vector<T>& v) {
|
PyObject* pythonize_vector(const T& v) {
|
||||||
const size_t size = v.size();
|
const size_t size = v.size();
|
||||||
PyObject* pyobj = PyTuple_New(size);
|
PyObject* pyobj = PyTuple_New(size);
|
||||||
for (size_t i = 0; i < size; ++i) {
|
for (size_t i = 0; i < size; ++i) {
|
||||||
PyTuple_SetItem(pyobj, i, pythonize(v[i]));
|
if constexpr (is_std_vector_v<typename T::value_type>) {
|
||||||
}
|
PyTuple_SetItem(pyobj, i, pythonize_vector(v[i]));
|
||||||
return pyobj;
|
} else {
|
||||||
}
|
PyTuple_SetItem(pyobj, i, pythonize(v[i]));
|
||||||
|
}
|
||||||
template <typename T>
|
|
||||||
PyObject* pythonize_vector2(const std::vector< std::vector<T> >& v) {
|
|
||||||
const size_t size = v.size();
|
|
||||||
PyObject* pyobj = PyTuple_New(size);
|
|
||||||
for (size_t i = 0; i < size; ++i) {
|
|
||||||
PyTuple_SetItem(pyobj, i, pythonize_vector(v[i]));
|
|
||||||
}
|
}
|
||||||
return pyobj;
|
return pyobj;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,9 +38,7 @@
|
|||||||
try {
|
try {
|
||||||
$result = $1.array_->apply_visitor([](auto& v){
|
$result = $1.array_->apply_visitor([](auto& v){
|
||||||
using U = std::decay_t<decltype(v)>;
|
using U = std::decay_t<decltype(v)>;
|
||||||
if constexpr (is_std_vector_vector_v<U>) {
|
if constexpr (is_std_vector_v<U>) {
|
||||||
return pythonize_vector2(v);
|
|
||||||
} else if constexpr (is_std_vector_v<U>) {
|
|
||||||
return pythonize_vector(v);
|
return pythonize_vector(v);
|
||||||
} else if constexpr (std::is_same_v<U, EnumerationReference>) {
|
} else if constexpr (std::is_same_v<U, EnumerationReference>) {
|
||||||
return pythonize(std::string(v.value()));
|
return pythonize(std::string(v.value()));
|
||||||
@@ -70,10 +68,22 @@
|
|||||||
|
|
||||||
%define CREATE_VECTOR_TYPEMAP_OUT(template_type)
|
%define CREATE_VECTOR_TYPEMAP_OUT(template_type)
|
||||||
%typemap(out) std::vector<template_type> {
|
%typemap(out) std::vector<template_type> {
|
||||||
$result = pythonize_vector<template_type>($1);
|
$result = pythonize_vector<std::vector<template_type>>($1);
|
||||||
}
|
}
|
||||||
%typemap(out) const std::vector<template_type>& {
|
%typemap(out) const std::vector<template_type>& {
|
||||||
$result = pythonize_vector<template_type>(*$1);
|
$result = pythonize_vector<std::vector<template_type>>(*$1);
|
||||||
|
}
|
||||||
|
%typemap(out) std::vector<std::vector<template_type>> {
|
||||||
|
$result = pythonize_vector<std::vector<std::vector<template_type>>>($1);
|
||||||
|
}
|
||||||
|
%typemap(out) const std::vector<std::vector<template_type>>& {
|
||||||
|
$result = pythonize_vector<std::vector<std::vector<template_type>>>(*$1);
|
||||||
|
}
|
||||||
|
%typemap(out) std::vector<std::vector<std::vector<template_type>>> {
|
||||||
|
$result = pythonize_vector<std::vector<std::vector<std::vector<template_type>>>>($1);
|
||||||
|
}
|
||||||
|
%typemap(out) const std::vector<std::vector<std::vector<template_type>>>& {
|
||||||
|
$result = pythonize_vector<std::vector<std::vector<std::vector<template_type>>>>(*$1);
|
||||||
}
|
}
|
||||||
%enddef
|
%enddef
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user