| 263 | } |
| 264 | |
| 265 | void TtlWktSerializer::write(const IfcGeom::TriangulationElement* o) |
| 266 | { |
| 267 | filename_.stream << ttl_object_id(o) << " a geo:Feature ;\n"; |
| 268 | filename_.stream << " dcterms:identifier " << escape_for_turtle( |
| 269 | IfcUtil::convert_utf8(o->guid())) << " ;\n"; |
| 270 | filename_.stream << " rdfs:label " << escape_for_turtle( |
| 271 | IfcUtil::convert_utf8(o->name()) |
| 272 | ) << " ;\n"; |
| 273 | filename_.stream << " geo:hasGeometry " << ttl_object_id(o, "_geometry") << " .\n\n"; |
| 274 | |
| 275 | if (!o->geometry().polyhedral_faces_with_holes().empty()) { |
| 276 | filename_.stream << ttl_object_id(o, "_geometry") << " a geo:Geometry ;\n"; |
| 277 | filename_.stream << " geo:asWKT " << escape_for_turtle( |
| 278 | IfcUtil::convert_utf8( |
| 279 | capture_output( |
| 280 | emit_polyhedral_surface, |
| 281 | o->geometry().verts(), |
| 282 | o->geometry().polyhedral_faces_with_holes())) |
| 283 | ) << "^^geo:wktLiteral .\n\n"; |
| 284 | |
| 285 | Eigen::Map<const Eigen::Matrix<double, 3, Eigen::Dynamic>> vertex_map(o->geometry().verts().data(), 3, o->geometry().verts().size() / 3); |
| 286 | |
| 287 | boost::optional<std::vector<std::vector<int>>::const_iterator> lowest_face; |
| 288 | double lowest_z = std::numeric_limits<double>::infinity(); |
| 289 | |
| 290 | for (const auto& f : o->geometry().polyhedral_faces_with_holes()) { |
| 291 | Eigen::Vector3d v0, v1, v2, v1_v0, v2_v0; |
| 292 | for (size_t i = 0; i < f[0].size(); ++i) { |
| 293 | v0 = vertex_map.transpose().row(f[0][0 + i]); |
| 294 | v1 = vertex_map.transpose().row(f[0][1 + i]); |
| 295 | v2 = vertex_map.transpose().row(f[0][2 + i]); |
| 296 | v1_v0 = v1 - v0; |
| 297 | v2_v0 = v2 - v0; |
| 298 | v1_v0.normalize(); |
| 299 | v2_v0.normalize(); |
| 300 | if ((std::abs(v1_v0.dot(v2_v0)) + 1.e-9) >= 1.0) { |
| 301 | // Don't derive normal from collinear edges |
| 302 | continue; |
| 303 | } |
| 304 | break; |
| 305 | } |
| 306 | |
| 307 | Eigen::Vector3d cross_product = v1_v0.cross(v2_v0); |
| 308 | cross_product.normalize(); |
| 309 | |
| 310 | // @nb we take abs because so that we can ignore face orientation and potential convatities rquire |
| 311 | if ((std::abs(cross_product.z()) + 1.e-9) >= 1.0 && v0.z() < lowest_z) { |
| 312 | lowest_face = f.begin(); |
| 313 | lowest_z = v0.z(); |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | if (lowest_face) { |
| 318 | filename_.stream << ttl_object_id(o) << " geo:hasGeometry " << ttl_object_id(o, "_footprint_geometry") << " .\n\n"; |
| 319 | filename_.stream << ttl_object_id(o, "_footprint_geometry") << " a geo:Geometry ;\n"; |
| 320 | filename_.stream << " geo:asWKT " << escape_for_turtle( |
| 321 | IfcUtil::convert_utf8( |
| 322 | capture_output( |
nothing calls this directly
no test coverage detected