mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +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;
|
||||
}
|
||||
|
||||
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 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>
|
||||
class IFC_GEOM_API SettingsContainer {
|
||||
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:
|
||||
settings_t settings;
|
||||
|
||||
@@ -470,73 +486,12 @@ namespace ifcopenshell {
|
||||
};
|
||||
|
||||
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
|
||||
namespace IfcGeom {
|
||||
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;
|
||||
if (iit->hasStyle()) {
|
||||
auto jt = std::find(_materials.begin(), _materials.end(), iit->StylePtr());
|
||||
if (jt == _materials.end()) {
|
||||
surface_style_id = (int)_materials.size();
|
||||
_materials.push_back(iit->StylePtr());
|
||||
auto jt = std::find(materials_.begin(), materials_.end(), iit->StylePtr());
|
||||
if (jt == materials_.end()) {
|
||||
surface_style_id = (int)materials_.size();
|
||||
materials_.push_back(iit->StylePtr());
|
||||
} 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) {
|
||||
const auto& material = IfcGeom::get_default_style(shape_model.entity());
|
||||
auto mit = std::find(_materials.begin(), _materials.end(), material);
|
||||
if (mit == _materials.end()) {
|
||||
surface_style_id = (int)_materials.size();
|
||||
_materials.push_back(material);
|
||||
auto mit = std::find(materials_.begin(), materials_.end(), material);
|
||||
if (mit == materials_.end()) {
|
||||
surface_style_id = (int)materials_.size();
|
||||
materials_.push_back(material);
|
||||
} 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 Y = convert ? (pY /unit_magnitude) : pY;
|
||||
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()) {
|
||||
const VertexKey key = std::make_tuple(item_id, material_index, X, Y, Z);
|
||||
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_);
|
||||
welds[key] = i;
|
||||
}
|
||||
_verts.push_back(X);
|
||||
_verts.push_back(Y);
|
||||
_verts.push_back(Z);
|
||||
verts_.push_back(X);
|
||||
verts_.push_back(Y);
|
||||
verts_.push_back(Z);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,14 +103,20 @@ namespace IfcGeom {
|
||||
typedef std::map<VertexKey, int> VertexKeyMap;
|
||||
typedef std::pair<int, int> Edge;
|
||||
|
||||
std::vector<double> _verts;
|
||||
std::vector<int> _faces;
|
||||
std::vector<int> _edges;
|
||||
std::vector<double> _normals;
|
||||
std::vector<double> verts_;
|
||||
|
||||
// @nb only one of these is populated based on settings, we didn't want to go
|
||||
// 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<int> _material_ids;
|
||||
std::vector<ifcopenshell::geometry::taxonomy::style::ptr> _materials;
|
||||
std::vector<int> _item_ids;
|
||||
std::vector<int> material_ids_;
|
||||
std::vector<ifcopenshell::geometry::taxonomy::style::ptr> materials_;
|
||||
std::vector<int> item_ids_;
|
||||
size_t weld_offset_;
|
||||
VertexKeyMap welds;
|
||||
|
||||
@@ -120,15 +126,17 @@ namespace IfcGeom {
|
||||
{}
|
||||
|
||||
public:
|
||||
const std::vector<double>& verts() const { return _verts; }
|
||||
const std::vector<int>& faces() const { return _faces; }
|
||||
const std::vector<int>& edges() const { return _edges; }
|
||||
const std::vector<double>& normals() const { return _normals; }
|
||||
const std::vector<double>& verts() const { return verts_; }
|
||||
const std::vector<int>& faces() const { return faces_; }
|
||||
const std::vector<std::vector<int>>& polyhedral_faces_without_holes() const { return polyhedral_faces_without_holes_; }
|
||||
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_; }
|
||||
const std::vector<double>& uvs() const { return uvs_; }
|
||||
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<int>& item_ids() const { return _item_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<int>& item_ids() const { return item_ids_; }
|
||||
|
||||
Triangulation(const BRep& shape_model);
|
||||
|
||||
@@ -146,14 +154,14 @@ namespace IfcGeom {
|
||||
const std::vector<int>& item_ids
|
||||
)
|
||||
: Representation(settings, entity, id)
|
||||
, _verts(verts)
|
||||
, _faces(faces)
|
||||
, _edges(edges)
|
||||
, _normals(normals)
|
||||
, verts_(verts)
|
||||
, faces_(faces)
|
||||
, edges_(edges)
|
||||
, normals_(normals)
|
||||
, uvs_(uvs)
|
||||
, _material_ids(material_ids)
|
||||
, _materials(materials)
|
||||
, _item_ids(item_ids)
|
||||
, material_ids_(material_ids)
|
||||
, materials_(materials)
|
||||
, item_ids_(item_ids)
|
||||
{}
|
||||
|
||||
virtual ~Triangulation() {}
|
||||
@@ -168,30 +176,44 @@ namespace IfcGeom {
|
||||
int addVertex(int item_index, int material_index, double X, double Y, double Z);
|
||||
|
||||
void addNormal(double X, double Y, double Z) {
|
||||
_normals.push_back(X);
|
||||
_normals.push_back(Y);
|
||||
_normals.push_back(Z);
|
||||
normals_.push_back(X);
|
||||
normals_.push_back(Y);
|
||||
normals_.push_back(Z);
|
||||
}
|
||||
|
||||
void addFace(int item_id, int style, int i0, int i1, int i2) {
|
||||
_faces.push_back(i0);
|
||||
_faces.push_back(i1);
|
||||
_faces.push_back(i2);
|
||||
faces_.push_back(i0);
|
||||
faces_.push_back(i1);
|
||||
faces_.push_back(i2);
|
||||
|
||||
_item_ids.push_back(item_id);
|
||||
_material_ids.push_back(style);
|
||||
item_ids_.push_back(item_id);
|
||||
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) {
|
||||
_edges.push_back(i0);
|
||||
_edges.push_back(i1);
|
||||
edges_.push_back(i0);
|
||||
edges_.push_back(i1);
|
||||
|
||||
_material_ids.push_back(style);
|
||||
material_ids_.push_back(style);
|
||||
}
|
||||
|
||||
void registerEdge(int i0, int i1) {
|
||||
_edges.push_back(i0);
|
||||
_edges.push_back(i1);
|
||||
edges_.push_back(i0);
|
||||
edges_.push_back(i1);
|
||||
}
|
||||
|
||||
void addEdge(int n1, int n2, std::map<std::pair<int, int>, int>& edgecount);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <BRepGProp.hxx>
|
||||
#include <GProp_GProps.hxx>
|
||||
#include <Geom_SphericalSurface.hxx>
|
||||
#include <Geom_Plane.hxx>
|
||||
|
||||
#include "OpenCascadeConversionResult.h"
|
||||
|
||||
@@ -15,6 +16,12 @@
|
||||
|
||||
#include <Standard_Version.hxx>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
#include <algorithm>
|
||||
|
||||
#if OCC_VERSION_HEX >= 0x70600
|
||||
#include <TopTools_FormatVersion.hxx>
|
||||
#endif
|
||||
@@ -24,6 +31,44 @@ using IfcGeom::OpaqueCoordinate;
|
||||
using IfcGeom::NumberNativeDouble;
|
||||
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 {
|
||||
// We bypass the conversion to gp_GTrsf, because it does not work
|
||||
void taxonomy_transform(const Eigen::Matrix4d* m, gp_XYZ& xyz) {
|
||||
@@ -35,6 +80,78 @@ namespace {
|
||||
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 {
|
||||
@@ -71,6 +188,18 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometr
|
||||
TopExp_Explorer exp;
|
||||
for (exp.Init(shape_, TopAbs_FACE); exp.More(); exp.Next(), ++num_faces) {
|
||||
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;
|
||||
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());
|
||||
*/
|
||||
|
||||
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[n2], dict[n3], edgecount);
|
||||
t->addEdge(dict[n3], dict[n1], edgecount);
|
||||
t->addEdge(dict[n1], dict[n2], edgecount);
|
||||
t->addEdge(dict[n2], dict[n3], edgecount);
|
||||
t->addEdge(dict[n3], dict[n1], edgecount);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& p : edgecount) {
|
||||
// @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()) {
|
||||
|
||||
@@ -101,6 +101,9 @@ SETTING = Literal[
|
||||
"piecewise-step-param",
|
||||
"use-python-opencascade",
|
||||
"no-parallel-mapping",
|
||||
"triangulation-type",
|
||||
"model-rotation",
|
||||
"model-offset",
|
||||
]
|
||||
SERIALIZER_SETTING = Literal[
|
||||
"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
|
||||
sys.path[0:1] = []
|
||||
|
||||
|
||||
import pytest
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.project
|
||||
@@ -75,3 +76,8 @@ class IFC2X3:
|
||||
|
||||
ifcopenshell.api.pre_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 %{
|
||||
# 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)
|
||||
material_ids = property(material_ids)
|
||||
materials = property(materials)
|
||||
|
||||
@@ -56,24 +56,6 @@ private:
|
||||
%rename("add") addEntity;
|
||||
%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 {};
|
||||
@@ -780,9 +762,7 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas
|
||||
PyObject* convert_cpp_attribute_to_python(AttributeValue arg) {
|
||||
return arg.array_->apply_visitor([](auto& v){
|
||||
using U = std::decay_t<decltype(v)>;
|
||||
if constexpr (is_std_vector_vector_v<U>) {
|
||||
return pythonize_vector2(v);
|
||||
} else if constexpr (is_std_vector_v<U>) {
|
||||
if constexpr (is_std_vector_v<U>) {
|
||||
return pythonize_vector(v);
|
||||
} else if constexpr (std::is_same_v<U, EnumerationReference>) {
|
||||
return pythonize(std::string(v.value()));
|
||||
|
||||
@@ -148,6 +148,24 @@
|
||||
#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.
|
||||
%feature("autodoc", "1");
|
||||
|
||||
|
||||
@@ -189,21 +189,15 @@
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
PyObject* pythonize_vector(const std::vector<T>& v) {
|
||||
PyObject* pythonize_vector(const 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(v[i]));
|
||||
}
|
||||
return pyobj;
|
||||
}
|
||||
|
||||
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]));
|
||||
if constexpr (is_std_vector_v<typename T::value_type>) {
|
||||
PyTuple_SetItem(pyobj, i, pythonize_vector(v[i]));
|
||||
} else {
|
||||
PyTuple_SetItem(pyobj, i, pythonize(v[i]));
|
||||
}
|
||||
}
|
||||
return pyobj;
|
||||
}
|
||||
|
||||
@@ -38,9 +38,7 @@
|
||||
try {
|
||||
$result = $1.array_->apply_visitor([](auto& v){
|
||||
using U = std::decay_t<decltype(v)>;
|
||||
if constexpr (is_std_vector_vector_v<U>) {
|
||||
return pythonize_vector2(v);
|
||||
} else if constexpr (is_std_vector_v<U>) {
|
||||
if constexpr (is_std_vector_v<U>) {
|
||||
return pythonize_vector(v);
|
||||
} else if constexpr (std::is_same_v<U, EnumerationReference>) {
|
||||
return pythonize(std::string(v.value()));
|
||||
@@ -70,10 +68,22 @@
|
||||
|
||||
%define CREATE_VECTOR_TYPEMAP_OUT(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>& {
|
||||
$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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user