mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-05 23:41:44 +00:00
ifcgeom: honour PnIndex in triangulated and polygonal face sets (#3434)
IfcTriangulatedFaceSet and IfcPolygonalFaceSet used CoordIndex values to
index Coordinates.CoordList directly, ignoring the optional PnIndex
attribute. When PnIndex is present it remaps point references, so a
CoordIndex value i must resolve as CoordList[PnIndex[i-1]-1] (both 1-based).
Without the indirection any model carrying a PnIndex was built from the wrong
points.
Add a resolve() helper in both mappings that applies the PnIndex indirection
when present and is a plain bounds-checked lookup otherwise, with bounds
checks at both index levels. When PnIndex is absent the behavior is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 3e55c5126c)
This commit is contained in:
committed by
Dion Moult
parent
c57da082a5
commit
956881bb99
@@ -39,8 +39,25 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPolygonalFaceSet& inst) {
|
||||
|
||||
int max_index = (int)points.size();
|
||||
|
||||
// When the optional PnIndex is present, CoordIndex values do not index into
|
||||
// CoordList directly but into PnIndex, which in turn remaps to CoordList.
|
||||
// Both index levels are 1-based per the IFC specification.
|
||||
auto pn_index = inst.PnIndex();
|
||||
auto resolve = [&](int64_t idx) -> const taxonomy::point3::ptr& {
|
||||
if (pn_index) {
|
||||
if (idx < 1 || idx > (int64_t)pn_index->size()) {
|
||||
throw ifcopenshell::exception("IfcPolygonalFaceSet PnIndex out of bounds for index " + boost::lexical_cast<std::string>(idx));
|
||||
}
|
||||
idx = (*pn_index)[idx - 1];
|
||||
}
|
||||
if (idx < 1 || idx > max_index) {
|
||||
throw ifcopenshell::exception("IfcPolygonalFaceSet index out of bounds for index " + boost::lexical_cast<std::string>(idx));
|
||||
}
|
||||
return points[idx - 1];
|
||||
};
|
||||
|
||||
auto shell = taxonomy::make<taxonomy::shell>();
|
||||
|
||||
|
||||
for (auto& f : polygonal_faces) {
|
||||
auto fa = taxonomy::make<taxonomy::face>();
|
||||
shell->children.push_back(fa);
|
||||
@@ -52,17 +69,14 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPolygonalFaceSet& inst) {
|
||||
auto indices = f.CoordIndex();
|
||||
taxonomy::point3::ptr previous;
|
||||
for (std::vector<int64_t>::const_iterator jt = indices.begin(); jt != indices.end(); ++jt) {
|
||||
if (*jt < 1 || *jt > max_index) {
|
||||
throw ifcopenshell::exception("IfcPolygonalFaceSet index out of bounds for index " + boost::lexical_cast<std::string>(*jt));
|
||||
}
|
||||
auto current = points[(*jt) - 1];
|
||||
auto current = resolve(*jt);
|
||||
if (jt != indices.begin()) {
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(previous, current));
|
||||
}
|
||||
previous = current;
|
||||
}
|
||||
if (!indices.empty()) {
|
||||
auto current = points[indices.front() - 1];
|
||||
auto current = resolve(indices.front());
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(previous, current));
|
||||
}
|
||||
}
|
||||
@@ -77,17 +91,14 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPolygonalFaceSet& inst) {
|
||||
loop->external = false;
|
||||
|
||||
for (std::vector<int64_t>::const_iterator jt = li.begin(); jt != li.end(); ++jt) {
|
||||
if (*jt < 1 || *jt > max_index) {
|
||||
throw ifcopenshell::exception("IfcPolygonalFaceSet index out of bounds for index " + boost::lexical_cast<std::string>(*jt));
|
||||
}
|
||||
auto current = points[(*jt) - 1];
|
||||
auto current = resolve(*jt);
|
||||
if (jt != li.begin()) {
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(previous, current));
|
||||
}
|
||||
previous = current;
|
||||
}
|
||||
if (!li.empty()) {
|
||||
auto current = points[li.front() - 1];
|
||||
auto current = resolve(li.front());
|
||||
loop->children.push_back(taxonomy::make<taxonomy::edge>(previous, current));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,23 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcTriangulatedFaceSet& inst) {
|
||||
|
||||
int max_index = (int)points.size();
|
||||
|
||||
// When the optional PnIndex is present, CoordIndex values do not index into
|
||||
// CoordList directly but into PnIndex, which in turn remaps to CoordList.
|
||||
// Both index levels are 1-based per the IFC specification.
|
||||
auto pn_index = inst->PnIndex();
|
||||
auto resolve = [&](int64_t idx) -> const taxonomy::point3::ptr& {
|
||||
if (pn_index) {
|
||||
if (idx < 1 || idx > (int64_t)pn_index->size()) {
|
||||
throw ifcopenshell::exception("IfcTriangulatedFaceSet PnIndex out of bounds for index " + boost::lexical_cast<std::string>(idx));
|
||||
}
|
||||
idx = (*pn_index)[idx - 1];
|
||||
}
|
||||
if (idx < 1 || idx > max_index) {
|
||||
throw ifcopenshell::exception("IfcTriangulatedFaceSet index out of bounds for index " + boost::lexical_cast<std::string>(idx));
|
||||
}
|
||||
return points[idx - 1];
|
||||
};
|
||||
|
||||
auto shell = taxonomy::make<taxonomy::shell>();
|
||||
|
||||
for (auto& indices : indices_list) {
|
||||
@@ -51,10 +68,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcTriangulatedFaceSet& inst) {
|
||||
loop->external = true;
|
||||
taxonomy::point3::ptr first, previous;
|
||||
for (std::vector<int64_t>::const_iterator jt = indices.begin(); jt != indices.end(); ++jt) {
|
||||
if (*jt < 1 || *jt > max_index) {
|
||||
throw ifcopenshell::exception("IfcTriangulatedFaceSet index out of bounds for index " + boost::lexical_cast<std::string>(*jt));
|
||||
}
|
||||
const taxonomy::point3::ptr& current = points[(*jt) - 1];
|
||||
const taxonomy::point3::ptr& current = resolve(*jt);
|
||||
if (jt == indices.begin()) {
|
||||
first = current;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user