mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Store edges representation item ids in Triangulation
This commit is contained in:
@@ -915,7 +915,7 @@ class Loader(bonsai.core.tool.Loader):
|
||||
#
|
||||
# we do `.tolist()` because Blender can't assign `np.int32` to it's custom attributes
|
||||
mesh["ios_edges"] = list(set(tuple(e) for e in ifcopenshell.util.shape.get_edges(geometry).tolist()))
|
||||
mesh["ios_item_ids"] = ifcopenshell.util.shape.get_representation_item_ids(geometry).tolist()
|
||||
mesh["ios_item_ids"] = ifcopenshell.util.shape.get_faces_representation_item_ids(geometry).tolist()
|
||||
|
||||
mesh.vertices.add(num_vertices)
|
||||
mesh.vertices.foreach_set("co", verts)
|
||||
|
||||
@@ -117,6 +117,7 @@ namespace IfcGeom {
|
||||
std::vector<int> material_ids_;
|
||||
std::vector<ifcopenshell::geometry::taxonomy::style::ptr> materials_;
|
||||
std::vector<int> item_ids_;
|
||||
std::vector<int> edges_item_ids_;
|
||||
size_t weld_offset_;
|
||||
VertexKeyMap welds;
|
||||
|
||||
@@ -137,6 +138,7 @@ namespace IfcGeom {
|
||||
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>& edges_item_ids() const { return edges_item_ids_; }
|
||||
|
||||
Triangulation(const BRep& shape_model);
|
||||
|
||||
@@ -152,6 +154,7 @@ namespace IfcGeom {
|
||||
const std::vector<int>& material_ids,
|
||||
const std::vector<ifcopenshell::geometry::taxonomy::style::ptr>& materials,
|
||||
const std::vector<int>& item_ids
|
||||
, const std::vector<int>& edges_item_ids
|
||||
)
|
||||
: Representation(settings, entity, id)
|
||||
, verts_(verts)
|
||||
@@ -162,6 +165,7 @@ namespace IfcGeom {
|
||||
, material_ids_(material_ids)
|
||||
, materials_(materials)
|
||||
, item_ids_(item_ids)
|
||||
, edges_item_ids_(edges_item_ids)
|
||||
{}
|
||||
|
||||
virtual ~Triangulation() {}
|
||||
@@ -204,16 +208,18 @@ namespace IfcGeom {
|
||||
material_ids_.push_back(style);
|
||||
}
|
||||
|
||||
void addEdge(int style, int i0, int i1) {
|
||||
void addEdge(int item_id, int style, int i0, int i1) {
|
||||
edges_.push_back(i0);
|
||||
edges_.push_back(i1);
|
||||
|
||||
material_ids_.push_back(style);
|
||||
edges_item_ids_.push_back(item_id);
|
||||
}
|
||||
|
||||
void registerEdge(int i0, int i1) {
|
||||
void registerEdge(int item_id, int i0, int i1) {
|
||||
edges_.push_back(i0);
|
||||
edges_.push_back(i1);
|
||||
edges_item_ids_.push_back(item_id);
|
||||
}
|
||||
|
||||
void registerEdgeCount(int n1, int n2, std::map<std::pair<int, int>, int>& edgecount);
|
||||
|
||||
@@ -303,7 +303,7 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometr
|
||||
// @todo should be != 2?
|
||||
if (p.second == 1 && emitted_edges.find(p.first) == emitted_edges.end()) {
|
||||
// non manifold edge, face boundary
|
||||
t->registerEdge(p.first.first, p.first.second);
|
||||
t->registerEdge(item_id, p.first.first, p.first.second);
|
||||
if (settings.get<settings::WeldVertices>().get()) {
|
||||
// only relevant while welding, because otherwise vertices are not shared among distinct faces
|
||||
emitted_edges.insert(p.first);
|
||||
@@ -386,7 +386,7 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometr
|
||||
}
|
||||
|
||||
for (auto& sgmt : segments) {
|
||||
t->addEdge(surface_style_id, sgmt.first, sgmt.second);
|
||||
t->addEdge(item_id, surface_style_id, sgmt.first, sgmt.second);
|
||||
}
|
||||
|
||||
previous = current;
|
||||
|
||||
@@ -300,11 +300,20 @@ def get_normals(geometry: ShapeType) -> npt.NDArray[np.float64]:
|
||||
return np.frombuffer(geometry.normals_buffer, dtype="d").reshape(-1, 3)
|
||||
|
||||
|
||||
def get_representation_item_ids(geometry: ShapeType) -> npt.NDArray[np.int32]:
|
||||
def get_faces_representation_item_ids(geometry: ShapeType) -> npt.NDArray[np.int32]:
|
||||
"""Get representation item ids for the geometry faces."""
|
||||
return np.frombuffer(geometry.item_ids_buffer, dtype="i")
|
||||
|
||||
|
||||
def get_edges_representation_item_ids(geometry: ShapeType) -> npt.NDArray[np.int32]:
|
||||
"""Get representation item ids for the geometry edges.
|
||||
|
||||
Can be useful for geometry without faces and in general is more universal
|
||||
since it's possible that geometry will have elements with and without faces.
|
||||
"""
|
||||
return np.frombuffer(geometry.edges_item_ids_buffer, dtype="i")
|
||||
|
||||
|
||||
def get_shape_vertices(shape: ShapeType, geometry: ShapeType) -> npt.NDArray[np.float64]:
|
||||
"""Get the shape's vertices as a numpy array
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import pytest
|
||||
import test.bootstrap
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.unit
|
||||
import ifcopenshell.api.root
|
||||
import ifcopenshell.api.context
|
||||
import ifcopenshell.api.project
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.api.owner.settings
|
||||
import ifcopenshell.api.project
|
||||
import ifcopenshell.api.root
|
||||
import ifcopenshell.api.unit
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.ifcopenshell_wrapper as W
|
||||
import ifcopenshell.util.shape
|
||||
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
||||
from typing import get_args
|
||||
|
||||
|
||||
@@ -47,6 +51,77 @@ class TestGeomSettings:
|
||||
assert "USE_PYTHON_OPENCASCADE" not in repr(settings)
|
||||
|
||||
|
||||
class TestTriangulationAttributes(test.bootstrap.IFC4):
|
||||
def test_faces_representation_item_ids(self):
|
||||
ifc_file = ifcopenshell.file()
|
||||
ifcopenshell.api.root.create_entity(ifc_file, ifc_class="IfcProject", name="Test")
|
||||
context = ifcopenshell.api.context.add_context(ifc_file, context_type="Model")
|
||||
|
||||
builder = ShapeBuilder(ifc_file)
|
||||
extrusion = builder.extrude(builder.rectangle(), magnitude=1.0)
|
||||
representation = builder.get_representation(context, extrusion)
|
||||
settings = ifcopenshell.geom.settings()
|
||||
shape = ifcopenshell.geom.create_shape(settings, representation)
|
||||
faces_item_ids = ifcopenshell.util.shape.get_faces_representation_item_ids(shape)
|
||||
faces = ifcopenshell.util.shape.get_faces(shape)
|
||||
assert set(faces_item_ids) == {extrusion.id()}
|
||||
assert len(faces) == 12 # Cube has 12 tris.
|
||||
assert len(faces_item_ids) == len(faces)
|
||||
|
||||
edges_item_ids = ifcopenshell.util.shape.get_edges_representation_item_ids(shape)
|
||||
edges = ifcopenshell.util.shape.get_edges(shape)
|
||||
assert set(edges_item_ids) == {extrusion.id()}
|
||||
assert len(edges) == 12 # Cube has 12 edges.
|
||||
assert len(edges_item_ids) == len(edges)
|
||||
|
||||
def test_curve_representation_item_ids(self):
|
||||
ifc_file = ifcopenshell.file()
|
||||
ifcopenshell.api.root.create_entity(ifc_file, ifc_class="IfcProject", name="Test")
|
||||
context = ifcopenshell.api.context.add_context(ifc_file, context_type="Model")
|
||||
|
||||
builder = ShapeBuilder(ifc_file)
|
||||
curve = builder.rectangle()
|
||||
representation = builder.get_representation(context, curve)
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("dimensionality", W.CURVES_SURFACES_AND_SOLIDS)
|
||||
shape = ifcopenshell.geom.create_shape(settings, representation)
|
||||
|
||||
faces_item_ids = ifcopenshell.util.shape.get_faces_representation_item_ids(shape)
|
||||
assert len(faces_item_ids) == 0
|
||||
|
||||
edges_item_ids = ifcopenshell.util.shape.get_edges_representation_item_ids(shape)
|
||||
edges = ifcopenshell.util.shape.get_edges(shape)
|
||||
assert set(edges_item_ids) == {curve.id()}
|
||||
assert len(edges) == 4
|
||||
assert len(edges_item_ids) == len(edges)
|
||||
|
||||
def test_mixed_representation_item_ids(self):
|
||||
ifc_file = ifcopenshell.file()
|
||||
ifcopenshell.api.root.create_entity(ifc_file, ifc_class="IfcProject", name="Test")
|
||||
context = ifcopenshell.api.context.add_context(ifc_file, context_type="Model")
|
||||
|
||||
builder = ShapeBuilder(ifc_file)
|
||||
curve = builder.rectangle()
|
||||
|
||||
fill = ifc_file.create_entity("IfcAnnotationFillArea", builder.rectangle())
|
||||
representation = builder.get_representation(context, (curve, fill))
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("dimensionality", W.CURVES_SURFACES_AND_SOLIDS)
|
||||
shape = ifcopenshell.geom.create_shape(settings, representation)
|
||||
|
||||
faces_item_ids = ifcopenshell.util.shape.get_faces_representation_item_ids(shape)
|
||||
faces = ifcopenshell.util.shape.get_faces(shape)
|
||||
assert len(faces) == 2 # Fill area will produce a triangulated face.
|
||||
assert set(faces_item_ids) == {fill.id()}
|
||||
assert len(faces_item_ids) == len(faces)
|
||||
|
||||
edges_item_ids = ifcopenshell.util.shape.get_edges_representation_item_ids(shape)
|
||||
edges = ifcopenshell.util.shape.get_edges(shape)
|
||||
assert set(edges_item_ids) == {fill.id(), curve.id()}
|
||||
assert len(edges) == 8 # 4 edges rectangle curve + 4 edges fill area
|
||||
assert len(edges_item_ids) == len(edges)
|
||||
|
||||
|
||||
class TestAssignObject:
|
||||
def test_no_welding_on_distinct_items(self):
|
||||
self.file = ifcopenshell.api.project.create_file()
|
||||
|
||||
@@ -647,6 +647,10 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
|
||||
return vector_to_buffer(self->item_ids());
|
||||
}
|
||||
|
||||
std::pair<const char*, size_t> edges_item_ids_buffer() const {
|
||||
return vector_to_buffer(self->edges_item_ids());
|
||||
}
|
||||
|
||||
std::pair<const char*, size_t> verts_buffer() const {
|
||||
return vector_to_buffer(self->verts());
|
||||
}
|
||||
@@ -703,6 +707,7 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
|
||||
edges_buffer = property(edges_buffer)
|
||||
material_ids_buffer = property(material_ids_buffer)
|
||||
item_ids_buffer = property(item_ids_buffer)
|
||||
edges_item_ids_buffer = property(edges_item_ids_buffer)
|
||||
verts_buffer = property(verts_buffer)
|
||||
normals_buffer = property(normals_buffer)
|
||||
colors_buffer = property(colors_buffer)
|
||||
|
||||
@@ -398,6 +398,7 @@ IfcGeom::Element* HdfSerializer::read(IfcParse::IfcFile& f, const std::string& g
|
||||
auto uvcoords = read_dataset<double>(meshGroup, DATASET_NAME_UVCOORDS);
|
||||
auto material_ids = read_dataset<int>(meshGroup, DATASET_NAME_MATERIAL_IDS);
|
||||
auto item_ids = read_dataset<int>(meshGroup, DATASET_NAME_ITEM_IDS);
|
||||
auto edges_item_ids = read_dataset<int>(meshGroup, DATASET_NAME_EDGES_ITEM_IDS);
|
||||
|
||||
std::vector<surface_style_serialization> surface_styles;
|
||||
|
||||
@@ -435,6 +436,7 @@ IfcGeom::Element* HdfSerializer::read(IfcParse::IfcFile& f, const std::string& g
|
||||
material_ids,
|
||||
surface_style_ptrs,
|
||||
item_ids
|
||||
, edges_item_ids
|
||||
));
|
||||
|
||||
triangulation_cache_.insert({ representation_id_str, triangulation_geometry });
|
||||
@@ -696,6 +698,7 @@ const H5std_string HdfSerializer::DATASET_NAME_INDICES = "indices";
|
||||
const H5std_string HdfSerializer::DATASET_NAME_EDGES = "edges";
|
||||
const H5std_string HdfSerializer::DATASET_NAME_MATERIAL_IDS = "material_ids";
|
||||
const H5std_string HdfSerializer::DATASET_NAME_ITEM_IDS = "item_ids";
|
||||
const H5std_string HdfSerializer::DATASET_NAME_EDGES_ITEM_IDS = "edges_item_ids";
|
||||
const H5std_string HdfSerializer::DATASET_NAME_MATERIALS = "materials";
|
||||
const H5std_string HdfSerializer::DATASET_NAME_OCCT = "brep";
|
||||
const H5std_string HdfSerializer::DATASET_NAME_PLACEMENT = "placement";
|
||||
|
||||
@@ -47,6 +47,7 @@ private:
|
||||
static const H5std_string DATASET_NAME_EDGES;
|
||||
static const H5std_string DATASET_NAME_MATERIAL_IDS;
|
||||
static const H5std_string DATASET_NAME_ITEM_IDS;
|
||||
static const H5std_string DATASET_NAME_EDGES_ITEM_IDS;
|
||||
static const H5std_string DATASET_NAME_MATERIALS;
|
||||
static const H5std_string DATASET_NAME_OCCT;
|
||||
static const H5std_string DATASET_NAME_PLACEMENT;
|
||||
|
||||
Reference in New Issue
Block a user