IfcConvert Plug-in discovery for info print

This commit is contained in:
Thomas Krijnen
2026-04-19 12:32:10 +02:00
parent 6ee05b646b
commit 9e19735275
25 changed files with 1423 additions and 53 deletions
+1 -4
View File
@@ -1,10 +1,7 @@
# IfcConvert # IfcConvert
add_executable(IfcConvert IfcConvert.cpp) add_executable(IfcConvert IfcConvert.cpp)
target_link_libraries(IfcConvert PRIVATE IfcGeom IfcParse ${OpenCASCADE_LIBRARIES} ${Boost_LIBRARIES} ${HDF5_LIBRARIES} ${USD_LIBRARIES}) target_link_libraries(IfcConvert PRIVATE IfcGeom IfcParse ${OpenCASCADE_LIBRARIES} ${Boost_LIBRARIES})
if(TARGET geometry_serializer_hdf)
target_link_libraries(IfcConvert PRIVATE geometry_serializer_hdf)
endif()
if(kernel_libraries OR mapping_libraries OR geometry_serializer_libraries OR document_serializer_libraries) if(kernel_libraries OR mapping_libraries OR geometry_serializer_libraries OR document_serializer_libraries)
add_dependencies(IfcConvert ${kernel_libraries} ${mapping_libraries} ${geometry_serializer_libraries} ${document_serializer_libraries}) add_dependencies(IfcConvert ${kernel_libraries} ${mapping_libraries} ${geometry_serializer_libraries} ${document_serializer_libraries})
endif() endif()
+115 -41
View File
@@ -50,11 +50,13 @@
#include <boost/optional/optional_io.hpp> #include <boost/optional/optional_io.hpp>
#include <boost/make_shared.hpp> #include <boost/make_shared.hpp>
#include <algorithm>
#include <fstream> #include <fstream>
#include <sstream>
#include <set>
#include <time.h>
#include <iomanip> #include <iomanip>
#include <map>
#include <set>
#include <sstream>
#include <time.h>
#if USE_VLD #if USE_VLD
#include <vld.h> #include <vld.h>
@@ -85,6 +87,91 @@ const std::string TEMP_FILE_EXTENSION = ".tmp";
namespace po = boost::program_options; namespace po = boost::program_options;
namespace {
struct serializer_usage_line {
std::string extensions;
std::string name;
std::string description;
};
std::string join_extensions(const std::vector<std::string>& extensions) {
std::ostringstream stream;
for (std::size_t i = 0; i < extensions.size(); ++i) {
if (i != 0) {
stream << ", ";
}
stream << extensions[i];
}
return stream.str();
}
void print_serializer_section(const char* title, std::vector<serializer_usage_line> lines) {
if (lines.empty()) {
return;
}
std::size_t extensions_width = 0;
std::size_t name_width = 0;
for (const auto& line : lines) {
extensions_width = std::max(extensions_width, line.extensions.size());
name_width = std::max(name_width, line.name.size());
}
cout_ << title << "\n";
for (const auto& line : lines) {
const auto extensions = ifcopenshell::path::from_utf8(line.extensions);
const auto name = ifcopenshell::path::from_utf8(line.name);
const auto description = ifcopenshell::path::from_utf8(line.description);
cout_ << " "
<< std::left << std::setw(static_cast<int>(extensions_width + 2)) << extensions
<< std::setw(static_cast<int>(name_width + 2)) << name
<< description << "\n";
}
cout_ << "\n";
}
std::vector<serializer_usage_line> geometry_serializer_usage_lines() {
auto serializers = ifcopenshell::serializers::geometry_serializer_registry_instance().serializers();
std::sort(serializers.begin(), serializers.end(), [](const auto& a, const auto& b) {
const auto& lhs = a.extensions.empty() ? a.format : a.extensions.front();
const auto& rhs = b.extensions.empty() ? b.format : b.extensions.front();
if (lhs != rhs) {
return lhs < rhs;
}
return a.format < b.format;
});
std::vector<serializer_usage_line> lines;
lines.reserve(serializers.size());
for (const auto& info : serializers) {
lines.push_back({ join_extensions(info.extensions), info.name, info.description });
}
return lines;
}
std::vector<serializer_usage_line> document_serializer_usage_lines() {
auto serializers = ifcopenshell::serializers::document_serializer_registry_instance().serializers();
std::sort(serializers.begin(), serializers.end(), [](const auto& a, const auto& b) {
if (a.format != b.format) {
return a.format < b.format;
}
return a.schema_name < b.schema_name;
});
std::set<std::string> seen_formats;
std::vector<serializer_usage_line> lines;
for (const auto& info : serializers) {
if (!seen_formats.insert(info.format).second) {
continue;
}
lines.push_back({ "." + info.format, info.name, info.description });
}
return lines;
}
}
void print_version() void print_version()
{ {
cout_ << "IfcOpenShell IfcConvert " << IFCOPENSHELL_VERSION; cout_ << "IfcOpenShell IfcConvert " << IFCOPENSHELL_VERSION;
@@ -98,31 +185,11 @@ void print_usage(bool suggest_help = true)
{ {
cout_ << "Usage: IfcConvert [options] <input.ifc> [<output>]\n" cout_ << "Usage: IfcConvert [options] <input.ifc> [<output>]\n"
<< "\n" << "\n"
<< "Converts (the geometry in) an IFC file into one of the following formats:\n" << "Converts (the geometry in) an IFC file into one of the following formats:\n\n";
<< " .obj WaveFront OBJ (a .mtl file is also created)\n" print_serializer_section("Geometry serializers:", geometry_serializer_usage_lines());
#ifdef WITH_OPENCOLLADA print_serializer_section("Document serializers:", document_serializer_usage_lines());
<< " .dae Collada Digital Assets Exchange\n" print_serializer_section("Built-in:", { { ".ifc", "IFC-SPF", "Industry Foundation Classes." } });
#endif cout_
#ifdef WITH_GLTF
<< " .glb glTF Binary glTF v2.0\n"
#endif
#ifdef WITH_USD
<< " .usd USD Universal Scene Description\n"
#endif
<< " .stp STEP Standard for the Exchange of Product Data\n"
<< " .igs IGES Initial Graphics Exchange Specification\n"
<< " .xml XML Property definitions and decomposition tree\n"
#ifdef WITH_GLTF
<< " .json JSON Property definitions and decomposition tree in xeokit json format\n"
#endif
<< " .rdb RocksDB RocksDB Key-Value store serialization of IFC data\n"
<< " .svg SVG Scalable Vector Graphics (2D floor plan)\n"
#ifdef WITH_HDF5
<< " .h5 HDF Hierarchical Data Format storing positions, normals and indices\n"
#endif
<< " .ttl TTL/WKT RDF Turtle with Well-Known-Text geometry\n"
<< " .ifc IFC-SPF Industry Foundation Classes\n"
<< "\n"
<< "If no output filename given, <input>" << ifcopenshell::path::from_utf8(DEFAULT_EXTENSION) << " will be used as the output file.\n"; << "If no output filename given, <input>" << ifcopenshell::path::from_utf8(DEFAULT_EXTENSION) << " will be used as the output file.\n";
if (suggest_help) { if (suggest_help) {
cout_ << "\nRun 'IfcConvert --help' for more information."; cout_ << "\nRun 'IfcConvert --help' for more information.";
@@ -224,6 +291,13 @@ int main(int argc, char** argv) {
path_t cache_file; path_t cache_file;
std::string log_format; std::string log_format;
std::string geometry_kernel; std::string geometry_kernel;
auto& document_serializer_registry = ifcopenshell::serializers::document_serializer_registry_instance();
auto& geometry_serializer_registry = ifcopenshell::serializers::geometry_serializer_registry_instance();
#ifdef WITH_HDF5
const bool supports_geometry_cache = geometry_serializer_registry.has(".h5");
#else
const bool supports_geometry_cache = false;
#endif
po::options_description generic_options("Command line options"); po::options_description generic_options("Command line options");
verbosity_counter vcounter; verbosity_counter vcounter;
@@ -232,14 +306,15 @@ int main(int argc, char** argv) {
("version", "display version information") ("version", "display version information")
("verbose,v", po::value(&vcounter)->zero_tokens(), "more verbose log messages. Use twice (-vv) for debugging level.") ("verbose,v", po::value(&vcounter)->zero_tokens(), "more verbose log messages. Use twice (-vv) for debugging level.")
("quiet,q", "less status and progress output") ("quiet,q", "less status and progress output")
#ifdef WITH_HDF5
("cache", "cache geometry creation. Use --cache-file to specify cache file path.")
#endif
("stderr-progress", "output progress to stderr stream") ("stderr-progress", "output progress to stderr stream")
("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)") ("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)")
("no-progress", "suppress possible progress bar type of prints that use carriage return") ("no-progress", "suppress possible progress bar type of prints that use carriage return")
("log-format", po::value<std::string>(&log_format), "log format: plain or json") ("log-format", po::value<std::string>(&log_format), "log format: plain or json")
("log-file", new po::typed_value<path_t, char_t>(&log_file), "redirect log output to file"); ("log-file", new po::typed_value<path_t, char_t>(&log_file), "redirect log output to file");
if (supports_geometry_cache) {
generic_options.add_options()
("cache", "cache geometry creation. Use --cache-file to specify cache file path.");
}
po::options_description fileio_options; po::options_description fileio_options;
fileio_options.add_options() fileio_options.add_options()
@@ -248,11 +323,12 @@ int main(int argc, char** argv) {
#endif #endif
("input-file", new po::typed_value<path_t, char_t>(0), "input IFC file") ("input-file", new po::typed_value<path_t, char_t>(0), "input IFC file")
("output-file", new po::typed_value<path_t, char_t>(0), "output geometry file") ("output-file", new po::typed_value<path_t, char_t>(0), "output geometry file")
#ifdef WITH_HDF5
("cache-file", new po::typed_value<path_t, char_t>(&cache_file), "geometry cache file")
#endif
("stream", "Use streaming conversion (currently supported with conversion to RocksDB)") ("stream", "Use streaming conversion (currently supported with conversion to RocksDB)")
; ;
if (supports_geometry_cache) {
fileio_options.add_options()
("cache-file", new po::typed_value<path_t, char_t>(&cache_file), "geometry cache file");
}
po::options_description ifc_options("IFC options"); po::options_description ifc_options("IFC options");
ifc_options.add_options() ifc_options.add_options()
@@ -534,11 +610,8 @@ int main(int argc, char** argv) {
boost::optional<std::list<IfcGeom::Element*>> elems_from_adaptor; boost::optional<std::list<IfcGeom::Element*>> elems_from_adaptor;
const path_t CACHE = ifcopenshell::path::from_utf8(".cache"), const path_t IFC = ifcopenshell::path::from_utf8(".ifc");
HDF = ifcopenshell::path::from_utf8(".h5"),
IFC = ifcopenshell::path::from_utf8(".ifc");
auto& document_serializer_registry = ifcopenshell::serializers::document_serializer_registry_instance();
const auto* document_serializer_info = document_serializer_registry.find(output_extension_utf8); const auto* document_serializer_info = document_serializer_registry.find(output_extension_utf8);
if (document_serializer_info) { if (document_serializer_info) {
int exit_code = EXIT_FAILURE; int exit_code = EXIT_FAILURE;
@@ -613,7 +686,6 @@ int main(int argc, char** argv) {
return exit_code; return exit_code;
} }
auto& geometry_serializer_registry = ifcopenshell::serializers::geometry_serializer_registry_instance();
const auto* geometry_serializer_info = geometry_serializer_registry.find(output_extension_utf8); const auto* geometry_serializer_info = geometry_serializer_registry.find(output_extension_utf8);
if (!geometry_serializer_info) { if (!geometry_serializer_info) {
cerr_ << "[error] Unknown output filename extension '" << output_extension << "'\n"; cerr_ << "[error] Unknown output filename extension '" << output_extension << "'\n";
@@ -830,10 +902,12 @@ int main(int argc, char** argv) {
context_iterator.reset(new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(ifc_file, geometry_kernel, geometry_settings), geometry_settings, ifc_file, filter_funcs, num_threads)); context_iterator.reset(new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(ifc_file, geometry_kernel, geometry_settings), geometry_settings, ifc_file, filter_funcs, num_threads));
} }
#if defined(WITH_HDF5) && defined(IFOPSH_WITH_OPENCASCADE) #ifdef WITH_HDF5
boost::shared_ptr<GeometrySerializer> cache; boost::shared_ptr<GeometrySerializer> cache;
const bool use_cache = context_iterator && (vmap.count("cache-file") || vmap.count("cache")); const bool use_cache = supports_geometry_cache && context_iterator && (vmap.count("cache-file") || vmap.count("cache"));
if (use_cache) { if (use_cache) {
const path_t CACHE = ifcopenshell::path::from_utf8(".cache");
const path_t HDF = ifcopenshell::path::from_utf8(".h5");
if (!vmap.count("cache-file")) { if (!vmap.count("cache-file")) {
cache_file = input_filename + CACHE + HDF; cache_file = input_filename + CACHE + HDF;
} }
@@ -0,0 +1,186 @@
#ifdef IFOPSH_WITH_CGAL
#include "validation_utils.h"
using namespace ifcopenshell::geometry;
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/AABB_face_graph_triangle_primitive.h>
typedef Kernel_::FT FT;
typedef Kernel_::Point_3 Point;
typedef Kernel_::Segment_3 Segment;
typedef CGAL::Polyhedron_3<Kernel_> Polyhedron;
typedef CGAL::AABB_face_graph_triangle_primitive<Polyhedron> Primitive;
typedef CGAL::AABB_traits<Kernel_, Primitive> Traits;
typedef CGAL::AABB_tree<Traits> Tree;
typedef Tree::Point_and_primitive_id Point_and_primitive_id;
void fix_spaceboundaries(ifcopenshell::file& f, bool no_progress, bool quiet, bool stderr_progress) {
intersection_validator v(f, { "IfcWall", "IfcSpace", "IfcSlab", "IfcCovering" }, 1.e-5, no_progress, quiet, stderr_progress);
auto rels = f.instances_by_type("IfcRelSpaceBoundary");
std::map<std::pair<const ifcopenshell::IfcBaseClass*, const ifcopenshell::IfcBaseClass*>, const ifcopenshell::IfcBaseClass*> rel_by_space_elem;
if (rels) {
std::for_each(rels->begin(), rels->end(), [&rel_by_space_elem](const ifcopenshell::IfcBaseClass* rel) {
auto x = ((ifcopenshell::IfcBaseEntity*)rel)->get_value<ifcopenshell::IfcBaseClass*>("RelatingSpace");
try {
auto y = ((ifcopenshell::IfcBaseEntity*)rel)->get_value<ifcopenshell::IfcBaseClass*>("RelatedBuildingElement");
rel_by_space_elem.insert({ { x,y }, rel });
} catch (ifcopenshell::exception&) {
// RelatedBuildingElement can be NULL
}
});
}
std::set<const ifcopenshell::IfcBaseClass*> rels_encounted;
ifcopenshell::file f2("boundaries-triangulated.ifc");
if (!f2.good()) {
return;
}
ifcopenshell::geometry::Settings settings;
settings.get<ifcopenshell::geometry::settings::UseWorldCoords>().value = false;
settings.get<ifcopenshell::geometry::settings::WeldVertices>().value = false;
settings.get<ifcopenshell::geometry::settings::ReorientShells>().value = true;
settings.get<ifcopenshell::geometry::settings::ConvertBackUnits>().value = true;
settings.get<ifcopenshell::geometry::settings::IteratorOutput>().value = ifcopenshell::geometry::settings::NATIVE;
settings.get<ifcopenshell::geometry::settings::DisableOpeningSubtractions>().value = true;
ifcopenshell::geometry::Converter c("cgal", &f2, settings);
std::map<std::set<std::string>, std::vector<Kernel_::Point_3>> elem_to_space_boundary_coords;
for (auto& i : *f2.instances_by_type("IfcProduct")) {
auto n = ((ifcopenshell::IfcBaseEntity*)i)->get_value<std::string>("Name");
auto g1 = n.substr(0, 22);
auto g2 = n.substr(23);
auto item = c.mapping()->map(i);
if (taxonomy::cast<taxonomy::collection>(item)->children[0] == nullptr) {
continue;
}
auto shell = taxonomy::cast<taxonomy::shell>(taxonomy::cast<taxonomy::collection>(taxonomy::cast<taxonomy::collection>(item)->children[0])->children[0]);
for (auto& face : shell->children) {
for (auto& wire : face->children) {
for (auto& edge : wire->children) {
auto p3 = boost::get<taxonomy::point3::ptr>(edge->start);
auto p4 = taxonomy::cast<taxonomy::geom_item>(item)->matrix->ccomponents() * p3->ccomponents().homogeneous();
Kernel_::Point_3 P(p4(0), p4(1), p4(2));
elem_to_space_boundary_coords[{g1, g2}].emplace_back(P);
}
}
}
}
std::set< std::set<std::string> > guid_pairs_visited;
v([&rel_by_space_elem, &elem_to_space_boundary_coords, &guid_pairs_visited](const intersection_validator::Box& a, const intersection_validator::Box& b) {
std::ostringstream ss;
// ss << id_map[a.id()]->first->data().to_string() << "x" << id_map[b.id()]->first->data().to_string() << std::endl;
// auto x = id_map[a.id()]->second * id_map[b.id()]->second;
auto A = a.handle()->first;
auto B = b.handle()->first;
auto Aguid = A->get_value<std::string>("GlobalId");
auto Bguid = B->get_value<std::string>("GlobalId");
int space_count = 0;
if (A->declaration().name() == "IfcSpace") {
space_count += 1;
}
if (B->declaration().name() == "IfcSpace") {
space_count += 1;
}
if (space_count != 1) {
return;
}
ss << a.handle()->first->data().to_string() << "x" << a.handle()->first->data().to_string() << std::endl;
auto x = a.handle()->second * b.handle()->second;
if (x.is_empty()) {
return;
}
guid_pairs_visited.insert({ Aguid, Bguid });
cgal_shape_t x_poly;
x.convert_to_polyhedron(x_poly);
{
std::string fn = "computed_boundaries_" + Aguid + "_" + Bguid + ".off";
std::ofstream computed_boundaries(fn.c_str());
computed_boundaries.precision(17);
computed_boundaries << x_poly;
}
Tree tree(faces(x_poly).first, faces(x_poly).second, x_poly);
tree.accelerate_distance_queries();
auto itelem = elem_to_space_boundary_coords.find({ Aguid, Bguid });
if (itelem == elem_to_space_boundary_coords.end()) {
logger::error("Missing space boundary relationship " + Aguid + " " + Bguid);
return;
}
const auto& coords = itelem->second;
std::vector<double> distances;
std::transform(coords.begin(), coords.end(), std::back_inserter(distances), [&tree](const Kernel_::Point_3& p) {
return std::sqrt(CGAL::to_double(tree.squared_distance(p)));
});
bool valid = *std::max_element(distances.begin(), distances.end()) < 0.4;
if (!valid) {
logger::error("Wrong connection geometry " + Aguid + " " + Bguid);
}
/*{
remove_thickness r(x_poly);
std::string fn = "thin_computed_boundaries_" + Aguid + "_" + Bguid + ".off";
std::ofstream computed_boundaries(fn.c_str());
computed_boundaries.precision(17);
computed_boundaries << r.flattened;
}*/
/*
{
auto FN = s0 + "-" + s1 + "-" + std::to_string(i0) + "-" + std::to_string(i1) + "-sides-sb.off";
std::ofstream os(FN.c_str());
os.precision(17);
os << r.polyhedron2;
}
{
auto FN = s0 + "-" + s1 + "-" + std::to_string(i0) + "-" + std::to_string(i1) + "-flat-sb.off";
std::ofstream os(FN.c_str());
os.precision(17);
os << r.flattened;
}
*/
});
auto is_wall_space_or_slab = [&f](const std::string& g) {
auto decl = f.instance_by_guid(g)->declaration();
return decl.is("IfcWall") || decl.is("IfcSpace") || decl.is("IfcSlab");
};
for (auto& i : *f2.instances_by_type("IfcProduct")) {
auto n = ((ifcopenshell::IfcBaseEntity*)i)->get_value<std::string>("Name");
auto g1 = n.substr(0, 22);
auto g2 = n.substr(23);
if (is_wall_space_or_slab(g1) && is_wall_space_or_slab(g2) && guid_pairs_visited.find({ g1, g2 }) == guid_pairs_visited.end()) {
logger::error("Space boundary for non-bounding geometry " + g1 + " " + g2);
}
}
}
#endif
@@ -0,0 +1,238 @@
#ifdef IFOPSH_WITH_CGAL
#include "../ifcgeom/kernels/cgal/CgalKernel.h"
#include "../ifcgeom/IfcGeomFilter.h"
#include "../ifcgeom/Iterator.h"
#include <CGAL/Polygon_mesh_processing/measure.h>
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <algorithm>
void fix_storeycontainment(ifcopenshell::file& f, bool no_progress, bool quiet, bool stderr_progress) {
ifcopenshell::geometry::Settings settings;
settings.get<ifcopenshell::geometry::settings::UseWorldCoords>().value = false;
settings.get<ifcopenshell::geometry::settings::WeldVertices>().value = false;
settings.get<ifcopenshell::geometry::settings::ReorientShells>().value = true;
settings.get<ifcopenshell::geometry::settings::ConvertBackUnits>().value = true;
settings.get<ifcopenshell::geometry::settings::IteratorOutput>().value = ifcopenshell::geometry::settings::NATIVE;
settings.get<ifcopenshell::geometry::settings::DisableOpeningSubtractions>().value = true;
std::vector<ifcopenshell::geometry::filter_t> no_openings_and_spaces = {
IfcGeom::entity_filter(false, false, {"IfcOpeningElement", "IfcSpace"})
};
IfcGeom::Iterator context_iterator("cgal", settings, &f, no_openings_and_spaces, 1);
auto get_elevation = [](const ifcopenshell::IfcBaseClass* a) {
return ((const ifcopenshell::IfcBaseEntity*)a)->get_value<double>("Elevation", 0.);
};
// latebound inverse attribute lookup not working
auto rels = f.instances_by_type("IfcRelContainedInSpatialStructure");
std::map<const ifcopenshell::IfcBaseClass*, const ifcopenshell::IfcBaseClass*> elem_to_storey;
std::for_each(rels->begin(), rels->end(), [&elem_to_storey](ifcopenshell::IfcBaseClass* r) {
auto elems = ((ifcopenshell::IfcBaseEntity*)r)->get_value<aggregate_of_instance::ptr>("RelatedElements");
auto storey = ((ifcopenshell::IfcBaseEntity*)r)->get_value<ifcopenshell::IfcBaseClass*>("RelatingStructure");
if (storey->declaration().name() == "IfcBuildingStorey") {
for (auto it = elems->begin(); it != elems->end(); ++it) {
elem_to_storey[*it] = storey;
}
}
});
auto storeys = f.instances_by_type("IfcBuildingStorey");
std::vector<const ifcopenshell::IfcBaseClass*> storeys_sorted(storeys->begin(), storeys->end());
std::sort(storeys_sorted.begin(), storeys_sorted.end(), [&get_elevation](const ifcopenshell::IfcBaseClass* a, const ifcopenshell::IfcBaseClass* b) {
return get_elevation(a) < get_elevation(b);
});
/*
std::wcout << "Storeys ";
for (auto& s : storeys_sorted) {
auto n = ((ifcopenshell::IfcBaseEntity*)s)->get_value<std::string>("Name");
std::wcout << n.c_str() << " ";
}
std::wcout << std::endl;
*/
std::vector<double> elevations;
std::transform(storeys_sorted.begin(), storeys_sorted.end(), std::back_inserter(elevations), get_elevation);
double LARGE = 1e4;
std::vector<std::pair<double, double>> elevation_slices;
for (size_t i = 0; i < elevations.size(); ++i) {
elevation_slices.push_back({
i == 0 ? -LARGE : elevations[i],
i + 1 == elevations.size() ? LARGE : elevations[i + 1]
});
}
std::for_each(elevation_slices.begin(), elevation_slices.end(), [](std::pair<double, double>& p) {
p.first -= 0.3;
p.second += 0.3;
});
std::vector<CGAL::Nef_polyhedron_3<Kernel_>> nefs;
std::transform(elevation_slices.begin(), elevation_slices.end(), std::back_inserter(nefs), [&LARGE](const std::pair<double, double>& p) {
// std::wcout << p.first << " - " << p.second << std::endl;
Kernel_::Point_3 p1(-LARGE, -LARGE, p.first);
Kernel_::Point_3 p2(+LARGE, +LARGE, p.second);
auto poly = ifcopenshell::geometry::utils::create_cube(p1, p2);
return ifcopenshell::geometry::utils::create_nef_polyhedron(poly);
});
/*
for (auto& n : nefs) {
auto poly = ifcopenshell::geometry::utils::create_polyhedron(n);
auto bounds = CGAL::Polygon_mesh_processing::bbox_3(poly);
for (int i = 0; i < 3; ++i) {
std::wcout << bounds.min(i) << std::endl;
}
for (int i = 0; i < 3; ++i) {
std::wcout << bounds.max(i) << std::endl;
}
std::wcout << "---" << std::endl;
}
*/
if (!context_iterator.initialize()) {
return;
}
size_t num_created = 0;
int old_progress = quiet ? 0 : -1;
for (;; ++num_created) {
bool has_more = true;
if (num_created) {
has_more = context_iterator.next();
}
IfcGeom::BRepElement* geom_object = nullptr;
if (has_more) {
geom_object = context_iterator.get_native();
}
if (!geom_object) {
break;
}
/*
std::stringstream ss;
ss << geom_object->product()->data().to_string();
auto sss = ss.str();
std::wcout << sss.c_str() << std::endl;
*/
if (elem_to_storey.find(geom_object->product()) == elem_to_storey.end()) {
// std::wcout << "not associated to storey" << std::endl;
continue;
}
std::vector<double> intersection_volumes(nefs.size());
for (auto& g : geom_object->geometry()) {
auto s = std::static_pointer_cast<ifcopenshell::geometry::CgalShape>(g.Shape())->poly();
const auto& m = g.Placement()->ccomponents();
const auto& n = geom_object->transformation().data()->ccomponents();
const cgal_placement_t trsf(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
m(2, 0), m(2, 1), m(2, 2), m(2, 3));
const cgal_placement_t trsf2(
n(0, 0), n(0, 1), n(0, 2), n(0, 3),
n(1, 0), n(1, 1), n(1, 2), n(1, 3),
n(2, 0), n(2, 1), n(2, 2), n(2, 3));
// Apply transformation
for (auto &vertex : vertices(s)) {
vertex->point() = vertex->point().transform(trsf).transform(trsf2);
}
/*
{
auto bounds = CGAL::Polygon_mesh_processing::bbox_3(s);
for (int i = 0; i < 3; ++i) {
std::wcout << bounds.min(i) << std::endl;
}
for (int i = 0; i < 3; ++i) {
std::wcout << bounds.max(i) << std::endl;
}
std::wcout << "---" << std::endl;
}
*/
CGAL::Nef_polyhedron_3<Kernel_> part_nef = ifcopenshell::geometry::utils::create_nef_polyhedron(s);
if (!part_nef.is_simple()) {
// std::wcout << "not simple" << std::endl;
continue;
}
std::vector<double>::iterator accumulator = intersection_volumes.begin();
std::for_each(nefs.begin(), nefs.end(), [&accumulator, &part_nef](const CGAL::Nef_polyhedron_3<Kernel_>& storey_nef) {
auto poly = ifcopenshell::geometry::utils::create_polyhedron(part_nef * storey_nef);
CGAL::Polygon_mesh_processing::triangulate_faces(poly);
*accumulator += CGAL::to_double(CGAL::Polygon_mesh_processing::volume(poly));
accumulator++;
});
}
/*
std::wcout << "volumes: ";
for (auto& v : intersection_volumes) {
std::wcout << v << " ";
}
std::wcout << std::endl;
*/
auto calc_idx = std::max_element(intersection_volumes.begin(), intersection_volumes.end()) - intersection_volumes.begin();
auto calc_overlap = intersection_volumes[calc_idx];
auto assigned_idx = std::distance(storeys_sorted.begin(), std::find(storeys_sorted.begin(), storeys_sorted.end(), elem_to_storey[geom_object->product()]));
auto assigned_overlap = intersection_volumes[assigned_idx];
if (calc_overlap > 0 && assigned_overlap < calc_overlap * 0.9) {
auto s = geom_object->product()->get_value<std::string>("GlobalId");
auto s1 = ((ifcopenshell::IfcBaseEntity*)storeys_sorted[calc_idx])->get_value<std::string>("GlobalId");
auto s2 = ((ifcopenshell::IfcBaseEntity*)elem_to_storey[geom_object->product()])->get_value<std::string>("GlobalId");
logger::error("Element " + s + " contained in " + s2 + " located on " + s1);
}
if (!no_progress) {
if (quiet) {
const int progress = context_iterator.progress();
for (; old_progress < progress; ++old_progress) {
std::cout << ".";
if (stderr_progress)
std::cerr << ".";
}
std::cout << std::flush;
if (stderr_progress)
std::cerr << std::flush;
} else {
const int progress = context_iterator.progress() / 2;
if (old_progress != progress) logger::progress_bar(progress);
old_progress = progress;
}
}
}
if (!no_progress && quiet) {
for (; old_progress < 100; ++old_progress) {
std::cout << ".";
if (stderr_progress)
std::cerr << ".";
}
std::cout << std::flush;
if (stderr_progress)
std::cerr << std::flush;
} else {
logger::status("\rDone fixing space boundaries for " + boost::lexical_cast<std::string>(num_created) +
" objects ");
}
}
#endif
@@ -0,0 +1,201 @@
#ifdef IFOPSH_WITH_CGAL
#include "validation_utils.h"
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <CGAL/Polygon_mesh_processing/measure.h>
#include <algorithm>
using namespace ifcopenshell::geometry;
void fix_wallconnectivity(ifcopenshell::file& f, bool no_progress, bool quiet, bool stderr_progress) {
intersection_validator v(f, { "IfcWall" }, 1.e-3, no_progress, quiet, stderr_progress);
ifcopenshell::geometry::Settings settings;
settings.get<ifcopenshell::geometry::settings::UseWorldCoords>().value = false;
settings.get<ifcopenshell::geometry::settings::WeldVertices>().value = false;
settings.get<ifcopenshell::geometry::settings::ReorientShells>().value = true;
settings.get<ifcopenshell::geometry::settings::ConvertBackUnits>().value = true;
settings.get<ifcopenshell::geometry::settings::IteratorOutput>().value = ifcopenshell::geometry::settings::NATIVE;
settings.get<ifcopenshell::geometry::settings::DisableOpeningSubtractions>().value = true;
settings.get<ifcopenshell::geometry::settings::IncludeCurves>().value = true;
settings.get<ifcopenshell::geometry::settings::IncludeSurfaces>().value = false;
ifcopenshell::geometry::Converter c("cgal", &f, settings);
auto rels = f.instances_by_type("IfcRelConnectsPathElements");
std::map<std::set<const ifcopenshell::IfcBaseClass*>, const ifcopenshell::IfcBaseClass*> rel_by_elem;
std::for_each(rels->begin(), rels->end(), [&rel_by_elem](const ifcopenshell::IfcBaseClass* rel) {
auto x = ((ifcopenshell::IfcBaseEntity*)rel)->get_value<ifcopenshell::IfcBaseClass*>("RelatingElement");
auto y = ((ifcopenshell::IfcBaseEntity*)rel)->get_value<ifcopenshell::IfcBaseClass*>("RelatedElement");
rel_by_elem.insert({{ x,y }, rel});
});
std::set<const ifcopenshell::IfcBaseClass*> rels_encounted;
double total_nef_intersection_time = 0.;
double conversion_to_poly = 0.;
v([&c, &rel_by_elem, &rels_encounted, &total_nef_intersection_time, &conversion_to_poly](const intersection_validator::Box& a, const intersection_validator::Box& b) {
auto A = a.handle()->first;
auto B = b.handle()->first;
const ifcopenshell::IfcBaseClass* rel = nullptr;
std::string a_type, b_type;
auto rit = rel_by_elem.find({ A, B });
if (rit != rel_by_elem.end()) {
rel = rit->second;
const bool a_is_relating = A == ((ifcopenshell::IfcBaseEntity*)rel)->get_value<ifcopenshell::IfcBaseClass*>("RelatingElement");
a_type = ((ifcopenshell::IfcBaseEntity*)rel)->get_value<std::string>("RelatingConnectionType");
b_type = ((ifcopenshell::IfcBaseEntity*)rel)->get_value<std::string>("RelatedConnectionType");
if (!a_is_relating) {
std::swap(a_type, b_type);
}
}
#if 0
auto a_poly = ifcopenshell::geometry::utils::create_polyhedron(a.handle()->second);
auto b_poly = ifcopenshell::geometry::utils::create_polyhedron(b.handle()->second);
std::wcout << "a" << std::endl;
for (auto& v : vertices(a_poly)) {
for (int i = 0; i < 3; ++i) {
std::wcout << CGAL::to_double(v->point().cartesian(i)) << " ";
}
std::wcout << std::endl;
}
std::wcout << "b" << std::endl;
for (auto& v : vertices(b_poly)) {
for (int i = 0; i < 3; ++i) {
std::wcout << CGAL::to_double(v->point().cartesian(i)) << " ";
}
std::wcout << std::endl;
}
#endif
std::ostringstream ss;
ss << A->data().to_string() << "x" << B->data().to_string() << std::endl;
std::clock_t intersection_begin = std::clock();
auto x = a.handle()->second * b.handle()->second;
std::clock_t intersection_end = std::clock();
total_nef_intersection_time += (intersection_end - intersection_begin) / (double) CLOCKS_PER_SEC;
if (x.is_empty()) {
return;
}
std::clock_t poly_begin = std::clock();
cgal_shape_t x_poly;
x.convert_to_polyhedron(x_poly);
std::clock_t poly_end = std::clock();
conversion_to_poly += (poly_end - poly_begin) / (double)CLOCKS_PER_SEC;
auto dza = a.bbox().zmax() - a.bbox().zmin();
auto dzb = b.bbox().zmax() - b.bbox().zmin();
auto bb = CGAL::Polygon_mesh_processing::bbox(x_poly);
if (bb.zmax() - bb.zmin() < std::min(dza, dzb) / 3.) {
return;
}
CGAL::Polygon_mesh_processing::triangulate_faces(x_poly);
if (CGAL::Polygon_mesh_processing::area(x_poly) > 4.0) {
return;
}
auto get_axis_parameter_min_max = [&c, &x_poly](const ifcopenshell::IfcBaseEntity* inst) {
auto item = c.mapping()->map(inst);
auto shaperep = taxonomy::cast<taxonomy::collection>(item)->children[0];
auto loop = taxonomy::dcast<taxonomy::loop>(taxonomy::cast<taxonomy::collection>(shaperep)->children[0]);
if (!loop) {
// std::wcout << "no suitable axis" << std::endl;
} else {
auto first_vertex = loop->children.front()->start;
auto last_vertex = loop->children.back()->end;
if (first_vertex.which() != 0 || last_vertex.which() != 0) {
// std::wcout << "trims not supported" << std::endl;
} else {
auto p0 = boost::get<taxonomy::point3::ptr>(first_vertex);
auto p1 = boost::get<taxonomy::point3::ptr>(last_vertex);
auto v0 = taxonomy::cast<taxonomy::geom_item>(item)->matrix->ccomponents() * p0->ccomponents().homogeneous();
auto v1 = taxonomy::cast<taxonomy::geom_item>(item)->matrix->ccomponents() * p1->ccomponents().homogeneous();
auto P0 = Kernel_::Point_3(v0(0), v0(1), v0(2));
auto P1 = Kernel_::Point_3(v1(0), v1(1), v1(2));
auto D = P1 - P0;
auto len = std::sqrt(CGAL::to_double(D.squared_length()));
D /= len;
std::vector<Kernel_::FT> parameters;
std::transform(vertices(x_poly).begin(), vertices(x_poly).end(), std::back_inserter(parameters), [&P0, D](cgal_vertex_descriptor_t& v) {
return (v->point() - P0) * D;
});
auto pit = std::minmax_element(parameters.begin(), parameters.end());
return std::make_pair(len, std::make_pair(CGAL::to_double(*pit.first), CGAL::to_double(*pit.second)));
}
}
const auto& nan = std::numeric_limits<double>::quiet_NaN();
return std::make_pair(nan, std::make_pair(nan, nan));
};
auto qualify_connection_type = [](double l, const std::pair<double, double>& p) {
if (p.first < 1.e-3) {
return "ATSTART";
} else if (p.second > l - 1.e-3) {
return "ATEND";
} else {
return "ATPATH";
}
};
auto alu0u1 = get_axis_parameter_min_max(A);
auto blu0u1 = get_axis_parameter_min_max(B);
auto atype_computed = qualify_connection_type(alu0u1.first, alu0u1.second);
auto btype_computed = qualify_connection_type(blu0u1.first, blu0u1.second);
rels_encounted.insert(rel);
if (a_type != atype_computed || b_type != btype_computed) {
if (rel) {
logger::error(std::string("Connection type ") + atype_computed + " " + btype_computed + " for:", rel);
} else {
auto A_str = A->get_value<std::string>("GlobalId");
auto B_str = B->get_value<std::string>("GlobalId");
logger::error("No connection for adjacent " + A_str + " " + B_str);
}
}
});
std::for_each(rels->begin(), rels->end(), [&rels_encounted, &v](const ifcopenshell::IfcBaseClass* rel) {
if (rels_encounted.find(rel) == rels_encounted.end()) {
auto x = (ifcopenshell::IfcBaseEntity*)((ifcopenshell::IfcBaseEntity*)rel)->get_value<ifcopenshell::IfcBaseClass*>("RelatingElement");
auto y = (ifcopenshell::IfcBaseEntity*)((ifcopenshell::IfcBaseEntity*)rel)->get_value<ifcopenshell::IfcBaseClass*>("RelatedElement");
if (v.successfully_processed.find(x) != v.successfully_processed.end() && v.successfully_processed.find(y) != v.successfully_processed.end()) {
logger::error("Connection for non-adjacent walls", rel);
}
}
});
std::wcout << std::setprecision(14);
std::wcout << "total_map_time " << v.total_map_time << std::endl;
std::wcout << "total_geom_time " << v.total_geom_time << std::endl;
std::wcout << "total_nef_time " << v.total_nef_time << std::endl;
std::wcout << "total_minkowsky_time " << v.total_minkowsky_time << std::endl;
std::wcout << "total_box_time " << v.total_box_time << std::endl;
std::wcout << "total_nef_intersection_time " << total_nef_intersection_time << std::endl;
std::wcout << "total_conversion_to_poly_time " << conversion_to_poly << std::endl;
}
#endif
+32
View File
@@ -0,0 +1,32 @@
#ifdef IFOPSH_WITH_CGAL
#include "validation_utils.h"
double facet_area(const cgal_shape_t::Facet_handle& f) {
auto p0 = f->facet_begin()->vertex()->point();
auto p1 = f->facet_begin()->next()->vertex()->point();
auto p2 = f->facet_begin()->next()->next()->vertex()->point();
return std::sqrt(CGAL::to_double(CGAL::cross_product(p0 - p1, p2 - p1).squared_length()));
}
void dump_facet(const cgal_shape_t::Facet_handle& f) {
auto p0 = f->facet_begin()->vertex()->point();
auto p1 = f->facet_begin()->next()->vertex()->point();
auto p2 = f->facet_begin()->next()->next()->vertex()->point();
auto V = CGAL::cross_product(p0 - p1, p2 - p1);
auto d = std::sqrt(CGAL::to_double(V.squared_length()));
if (d > 1.e-20) {
V /= d;
}
std::ostringstream oss;
oss.precision(8);
oss << "Facet with area " << facet_area(f) << " and normal ("
<< CGAL::to_double(V.cartesian(0)) << " " << CGAL::to_double(V.cartesian(1)) << " "
<< CGAL::to_double(V.cartesian(2)) << ")";
auto osss = oss.str();
std::wcout << osss.c_str() << std::endl;
}
#endif
+602
View File
@@ -0,0 +1,602 @@
#ifdef IFOPSH_WITH_CGAL
#include "../ifcgeom/kernels/cgal/CgalKernel.h"
#include "../ifcgeom/IfcGeomFilter.h"
#include "../ifcgeom/Iterator.h"
#include <CGAL/box_intersection_d.h>
#include <CGAL/minkowski_sum_3.h>
#include <CGAL/AABB_tree.h>
#if CGAL_VERSION_NR >= 1060000000
#include <CGAL/AABB_traits_3.h>
#else
#include <CGAL/AABB_traits.h>
#endif
#include <CGAL/Polyhedron_3.h>
#include <CGAL/AABB_face_graph_triangle_primitive.h>
#include <fstream>
#include <iostream>
#if CGAL_VERSION_NR >= 1060000000
#define variant_get std::get_if
#else
#define variant_get boost::get
#endif
template <typename T>
T enlarge(const T& t, double d = 1.e-5) {
typename T::NT min[3];
typename T::NT max[3];
for (int i = 0; i < t.dimension(); ++i) {
min[i] = t.min_coord(i) - d;
max[i] = t.max_coord(i) + d;
}
return T(min, max, t.handle());
}
template <class HDS>
struct Build_Offset : public CGAL::Modifier_base<HDS> {
std::list<cgal_shape_t::Facet_handle> input;
void operator()(HDS& hds) {
// Postcondition: hds is a valid polyhedral surface.
CGAL::Polyhedron_incremental_builder_3<HDS> B(hds);
int Nv = 0, Nf = 0;
for (auto& f : input) {
Nv += 3;
Nf += 1;
}
B.begin_surface(Nv, Nf);
for (auto& f : input) {
auto p0 = f->facet_begin()->vertex()->point();
auto p1 = f->facet_begin()->next()->vertex()->point();
auto p2 = f->facet_begin()->next()->next()->vertex()->point();
auto O = CGAL::centroid(p0, p1, p2);
Kernel_::Point_3* p012[3] = { &p0, &p1, &p2 };
for (int i = 0; i < 3; ++i) {
*p012[i] = CGAL::ORIGIN + (((*(p012[i])) - CGAL::ORIGIN) + ((*(p012[i])) - O));
B.add_vertex(*p012[i]);
}
}
Nv = 0;
for (int i = 0; i < Nf; ++i) {
B.begin_facet();
B.add_vertex_to_facet(Nv++);
B.add_vertex_to_facet(Nv++);
B.add_vertex_to_facet(Nv++);
B.end_facet();
}
B.end_surface();
}
};
template <typename Ts>
std::list<cgal_shape_t::Facet_handle> connected_faces(cgal_shape_t::Facet_handle f, const Ts& excluded) {
std::set<cgal_shape_t::Facet_handle> fs = { f };
std::function<void(cgal_shape_t::Facet_handle& f)> process;
process = [&fs, &process, &excluded](cgal_shape_t::Facet_handle& f) {
cgal_shape_t::Halfedge_around_facet_circulator circ = f->facet_begin(), end(circ);
do {
auto ff = circ->opposite()->facet();
if (excluded.find(ff) == excluded.end()) {
auto p = fs.insert(ff);
if (p.second) {
process(ff);
}
}
} while (++circ != end);
};
process(f);
return std::list<cgal_shape_t::Facet_handle>(fs.begin(), fs.end());
}
template <class HDS>
struct Builder_With_Map : public CGAL::Modifier_base<HDS> {
std::list<cgal_shape_t::Facet_handle> input;
std::map<Kernel_::Point_3, Kernel_::Point_3> mapping;
void operator()(HDS& hds) {
// Postcondition: hds is a valid polyhedral surface.
CGAL::Polyhedron_incremental_builder_3<HDS> B(hds);
std::set<Kernel_::Point_3> used_points;
for (auto& f : input) {
cgal_shape_t::Halfedge_around_facet_circulator circ = f->facet_begin(), end(circ);
do {
auto P = circ->vertex()->point();
auto it = mapping.find(P);
if (it == mapping.end()) {
std::wcout << "WARNING unprojected point :(" << std::endl;
} else {
P = it->second;
}
used_points.insert(P);
} while (++circ != end);
}
B.begin_surface(used_points.size(), input.size());
for (auto& p : used_points) {
B.add_vertex(p);
}
for (auto& f : input) {
B.begin_facet();
cgal_shape_t::Halfedge_around_facet_circulator circ = f->facet_begin(), end(circ);
do {
auto P = circ->vertex()->point();
auto it = mapping.find(P);
if (it == mapping.end()) {
std::wcout << "WARNING unprojected point :(" << std::endl;
} else {
P = it->second;
}
auto jt = used_points.find(P);
if (jt == used_points.end()) {
throw std::runtime_error("Unable to map point");
}
size_t idx = std::distance(used_points.begin(), jt);
std::wcout << "idx " << idx << std::endl;
B.add_vertex_to_facet(idx);
} while (++circ != end);
B.end_facet();
}
B.end_surface();
}
};
double facet_area(const cgal_shape_t::Facet_handle& f);
void dump_facet(const cgal_shape_t::Facet_handle& f);
struct remove_thickness {
typedef Kernel_::Point_3 Point;
typedef Kernel_::Plane_3 Plane;
typedef Kernel_::Vector_3 Vector;
typedef Kernel_::Segment_3 Segment;
typedef Kernel_::Ray_3 Ray;
typedef CGAL::Polyhedron_3<Kernel_> Polyhedron;
typedef CGAL::AABB_face_graph_triangle_primitive<Polyhedron> Primitive;
#if CGAL_VERSION_NR >= 1060000000
typedef CGAL::AABB_traits_3<Kernel_, Primitive> AAbbTraits;
#else
typedef CGAL::AABB_traits<Kernel_, Primitive> AAbbTraits;
#endif
typedef CGAL::AABB_tree<AAbbTraits> Tree;
typedef boost::optional<Tree::Intersection_and_primitive_id<Ray>::Type> Ray_intersection;
cgal_shape_t polyhedron, polyhedron2, flattened;
remove_thickness(const cgal_shape_t& p)
// edge_collapse(p) still does not work :(
: polyhedron(p)
, polyhedron2(p) {
CGAL::Polygon_mesh_processing::triangulate_faces(polyhedron);
CGAL::Polygon_mesh_processing::triangulate_faces(polyhedron2);
std::list<cgal_shape_t::Facet_handle> non_degenerate, degenerate, longitudinal;
std::set<cgal_shape_t::Facet_iterator> thin_sides;
std::wcout << "ALL FACES:" << std::endl;
for (auto& f : faces(polyhedron)) {
dump_facet(f);
if (facet_area(f) > 1.e-20) {
non_degenerate.push_back(f);
} else {
degenerate.push_front(f);
std::wcout << "Degenerate, area: " << facet_area(f) << std::endl;
}
}
std::wcout << "NON DEGENERATE:" << std::endl;
for (auto& f : non_degenerate) {
dump_facet(f);
}
cgal_shape_t enlarged_non_degenerate_triangles;
Build_Offset<cgal_shape_t::HDS> bo;
bo.input = non_degenerate;
enlarged_non_degenerate_triangles.delegate(bo);
// @todo, first on non-enlarged faces, then on enlarged; to fix projection on concave surfaces where the enlarging operation shortens projection distances.
Tree tree(faces(enlarged_non_degenerate_triangles).first, faces(enlarged_non_degenerate_triangles).second, enlarged_non_degenerate_triangles);
std::map<cgal_face_descriptor_t, Kernel_::Vector_3> face_normals;
boost::associative_property_map<std::map<cgal_face_descriptor_t, Kernel_::Vector_3>> face_normals_map(face_normals);
CGAL::Polygon_mesh_processing::compute_face_normals(polyhedron, face_normals_map);
for (auto& f : non_degenerate) {
auto O = CGAL::centroid(
f->facet_begin()->vertex()->point(),
f->facet_begin()->next()->vertex()->point(),
f->facet_begin()->next()->next()->vertex()->point()
);
Ray ray(O, -face_normals_map[f]);
std::list<Ray_intersection> intersections;
tree.all_intersections(ray, std::back_inserter(intersections));
double N = std::numeric_limits<double>::infinity();
Point P;
for (auto& intersection : intersections) {
if (variant_get<Point>(&(intersection->first))) {
const Point* p = variant_get<Point>(&(intersection->first));
const double d = std::sqrt(CGAL::to_double((*p - O).squared_length()));
if (d > 1.e-20 && d < N) {
N = d;
}
}
}
if (N != std::numeric_limits<double>::infinity() && N > 1.e-4) {
thin_sides.insert(f);
}
}
std::wcout << "THIN SIDES:" << std::endl;
for (auto& f : thin_sides) {
dump_facet(f);
}
for (auto& f : non_degenerate) {
if (thin_sides.find(f) == thin_sides.end()) {
longitudinal.push_back(f);
}
}
std::wcout << "LONGITUDONAL:" << std::endl;
for (auto& f : longitudinal) {
dump_facet(f);
}
std::wcout << "faces " << faces(polyhedron).size() << "long " << longitudinal.size() << "thin " << thin_sides.size() << "non-degen " << non_degenerate.size() << std::endl;
cgal_shape_t enlarged_indiv_triangles;
Build_Offset<cgal_shape_t::HDS> bo2;
bo2.input = longitudinal;
enlarged_indiv_triangles.delegate(bo2);
{
std::ofstream ofs("enlarged.off");
ofs.precision(17);
ofs << enlarged_indiv_triangles;
}
Tree tree2(faces(enlarged_indiv_triangles).begin(), faces(enlarged_indiv_triangles).end(), enlarged_indiv_triangles);
std::map<Kernel_::Point_3, Kernel_::Point_3> new_points;
for (Polyhedron::Facet_iterator fit = polyhedron.facets_begin();
fit != polyhedron.facets_end();
++fit) {
if (CGAL::collinear(
fit->halfedge()->vertex()->point(),
fit->halfedge()->next()->vertex()->point(),
fit->halfedge()->opposite()->vertex()->point())) {
std::wcout << "degenerate triangle" << std::endl;
}
}
for (auto& v : vertices(polyhedron)) {
auto O = v->point();
Kernel_::Vector_3 norm;
Kernel_::Vector_3 accum;
int count = 0;
CGAL::Face_around_target_circulator<cgal_shape_t> it(v->halfedge(), polyhedron), end(it);
do {
cgal_shape_t::Facet_handle fh = (*it)->halfedge()->facet();
auto jt = std::find(non_degenerate.begin(), non_degenerate.end(), fh);
std::wcout << "non degen: " << (jt != non_degenerate.end()) << std::endl;
auto kt = std::find(thin_sides.begin(), thin_sides.end(), fh);
std::wcout << "thin side: " << (kt != thin_sides.end()) << std::endl;
if (jt != non_degenerate.end() && kt == thin_sides.end()) {
// else degenerate, prevent div by zero, do not incorporate in vnorm.
// or else part of thin side
auto p0 = (*it)->facet_begin()->vertex()->point();
auto p1 = (*it)->facet_begin()->next()->vertex()->point();
auto p2 = (*it)->facet_begin()->next()->next()->vertex()->point();
{
std::ostringstream oss;
oss.precision(8);
oss << "p0 " << p0.cartesian(0) << " " << p0.cartesian(1) << " " << p0.cartesian(2) << "\n";
oss << "p1 " << p1.cartesian(0) << " " << p1.cartesian(1) << " " << p1.cartesian(2) << "\n";
oss << "p2 " << p2.cartesian(0) << " " << p2.cartesian(1) << " " << p2.cartesian(2) << "\n";
auto osss = oss.str();
std::wcout << osss.c_str() << std::endl;
}
auto fnorm = CGAL::cross_product(p0 - p1, p2 - p1);
fnorm /= std::sqrt(CGAL::to_double(fnorm.squared_length()));
// const auto& fnorm = face_normals_map_2[*it];
std::ostringstream oss;
oss.precision(8);
oss << fnorm.cartesian(0) << " " << fnorm.cartesian(1) << " " << fnorm.cartesian(2);
auto osss = oss.str();
std::wcout << osss.c_str() << std::endl;
accum += fnorm;
++count;
}
++it;
} while (it != end);
norm = accum / count;
std::wcout << "count " << count << std::endl;
if (count == 0) {
// part of only degenerate or only thin sides
continue;
}
// v->vertex_begin();
Ray ray(O, norm);
std::ostringstream oss;
oss.precision(8);
oss << O << " -> " << norm;
auto osss = oss.str();
std::wcout << osss.c_str() << std::endl;
std::list<Ray_intersection> intersections;
tree2.all_intersections(ray, std::back_inserter(intersections));
double N = std::numeric_limits<double>::infinity();
Point P;
bool used_intersection = false;
if (intersections.size()) {
for (auto& intersection : intersections) {
if (variant_get<Point>(&(intersection->first))) {
const Point* p = variant_get<Point>(&(intersection->first));
const double d = std::sqrt(CGAL::to_double((*p - O).squared_length()));
if (d < N && d > 1.e-20) {
N = d;
P = *p;
std::wcout << "intersection @ " << d << std::endl;
}
}
}
std::wcout << "-----------" << std::endl;
// average the new point
new_points[O] = CGAL::ORIGIN + (((O - CGAL::ORIGIN) + (P - CGAL::ORIGIN))) / 2;
used_intersection = true;
}
if (!used_intersection) {
std::wcout << "no intersection :(" << std::endl;
}
}
auto thin_sides_degenerate = thin_sides;
thin_sides_degenerate.insert(degenerate.begin(), degenerate.end());
// @todo choose connected / connected_opposing based on largest combined area of facets?
if (longitudinal.size() == 0) {
std::wcout << "no longitudinal faces detected :(" << std::endl;
return;
}
auto connected = connected_faces(*longitudinal.begin(), thin_sides_degenerate);
decltype(connected) connected_opposing;
for (auto& f : longitudinal) {
if (std::find(connected.begin(), connected.end(), f) == connected.end()) {
connected_opposing = connected_faces(f, thin_sides_degenerate);
std::set<cgal_shape_t::Facet_handle> longi(longitudinal.begin(), longitudinal.end());
std::set<cgal_shape_t::Facet_handle> both_sides(connected.begin(), connected.end());
both_sides.insert(connected_opposing.begin(), connected_opposing.end());
if (longi == both_sides) {
std::wcout << "Facet connection functioning properly" << std::endl;
} else {
std::wcout << "Facet connection functioning incorrectly" << std::endl;
}
break;
}
}
Builder_With_Map<cgal_shape_t::HDS> b2;
b2.input = connected;
b2.mapping = new_points;
flattened.delegate(b2);
}
};
struct intersection_validator {
typedef std::list<std::pair<const ifcopenshell::IfcBaseEntity*, CGAL::Nef_polyhedron_3<Kernel_>> > nefs_t;
typedef CGAL::Box_intersection_d::Box_with_handle_d<double, 3, nefs_t::value_type*> Box;
std::vector<Box> boxes;
nefs_t nefs;
double total_map_time = 0.;
double total_geom_time = 0.;
double total_nef_time = 0.;
double total_minkowsky_time = 0.;
double total_box_time = 0.;
std::set<const ifcopenshell::IfcBaseEntity*> successfully_processed;
intersection_validator(ifcopenshell::file& f, std::initializer_list<std::string> entities, double eps, bool no_progress, bool quiet, bool stderr_progress) {
ifcopenshell::geometry::Settings settings;
settings.get<ifcopenshell::geometry::settings::UseWorldCoords>().value = false;
settings.get<ifcopenshell::geometry::settings::WeldVertices>().value = false;
settings.get<ifcopenshell::geometry::settings::ReorientShells>().value = true;
settings.get<ifcopenshell::geometry::settings::ConvertBackUnits>().value = true;
settings.get<ifcopenshell::geometry::settings::IteratorOutput>().value = ifcopenshell::geometry::settings::NATIVE;
settings.get<ifcopenshell::geometry::settings::DisableOpeningSubtractions>().value = true;
std::vector<ifcopenshell::geometry::filter_t> spaces_and_walls = {
IfcGeom::entity_filter(true, false, entities)
};
IfcGeom::Iterator context_iterator("cgal", settings, &f, spaces_and_walls, 1);
if (!context_iterator.initialize()) {
return;
}
auto polycube = ifcopenshell::geometry::utils::create_cube(eps);
auto cube = ifcopenshell::geometry::utils::create_nef_polyhedron(polycube);
size_t num_created = 0;
int old_progress = quiet ? 0 : -1;
for (;; ++num_created) {
bool has_more = true;
if (num_created) {
has_more = context_iterator.next();
}
IfcGeom::BRepElement* geom_object = nullptr;
if (has_more) {
geom_object = context_iterator.get_native();
}
if (!geom_object) {
break;
}
std::stringstream ss;
geom_object->product()->to_string(ss);
auto sss = ss.str();
std::wcout << sss.c_str() << std::endl;
for (auto& g : geom_object->geometry()) {
cgal_shape_t s = *std::static_pointer_cast<ifcopenshell::geometry::CgalShape>(g.Shape());
const auto& m = g.Placement()->ccomponents();
const auto& n = geom_object->transformation().data()->ccomponents();
const cgal_placement_t trsf(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
m(2, 0), m(2, 1), m(2, 2), m(2, 3));
const cgal_placement_t trsf2(
n(0, 0), n(0, 1), n(0, 2), n(0, 3),
n(1, 0), n(1, 1), n(1, 2), n(1, 3),
n(2, 0), n(2, 1), n(2, 2), n(2, 3));
// Apply transformation
for (auto &vertex : vertices(s)) {
vertex->point() = vertex->point().transform(trsf).transform(trsf2);
}
std::clock_t nef_begin = std::clock();
CGAL::Nef_polyhedron_3<Kernel_> nef = ifcopenshell::geometry::utils::create_nef_polyhedron(s);
std::clock_t nef_end = std::clock();
total_nef_time += (nef_end - nef_begin) / (double) CLOCKS_PER_SEC;
if (nef.is_empty()) {
std::wcout << "Failed to create nef" << std::endl;
continue;
}
successfully_processed.insert(geom_object->product());
nef = CGAL::minkowski_sum_3(nef, cube);
std::clock_t minkowski_end = std::clock();
total_minkowsky_time += (minkowski_end - nef_end) / (double) CLOCKS_PER_SEC;
std::wcout << "product: " << geom_object->product() << std::endl;
nefs.push_back({ geom_object->product(), nef });
Box b(&*(nefs.rbegin()));
// id_map[b.id()] = ;
for (auto &vertex : vertices(s)) {
double p[3] = {
CGAL::to_double(vertex->point().cartesian(0)),
CGAL::to_double(vertex->point().cartesian(1)),
CGAL::to_double(vertex->point().cartesian(2))
};
b.extend(p);
}
boxes.push_back(enlarge(b));
/*
std::ostringstream ss;
ss << geom_object->product()->data().to_string() << std::endl << b.min_coord(0) << " - " << b.max_coord(0) << std::endl;
auto sss = ss.str();
std::wcout << sss.c_str();
*/
}
if (!no_progress) {
if (quiet) {
const int progress = context_iterator.progress();
for (; old_progress < progress; ++old_progress) {
std::cout << ".";
if (stderr_progress)
std::cerr << ".";
}
std::cout << std::flush;
if (stderr_progress)
std::cerr << std::flush;
} else {
const int progress = context_iterator.progress() / 2;
if (old_progress != progress) logger::progress_bar(progress);
old_progress = progress;
}
}
}
if (!no_progress && quiet) {
for (; old_progress < 100; ++old_progress) {
std::cout << ".";
if (stderr_progress)
std::cerr << ".";
}
std::cout << std::flush;
if (stderr_progress)
std::cerr << std::flush;
} else {
logger::status("\rDone fixing space boundaries for " + boost::lexical_cast<std::string>(num_created) +
" objects ");
}
/*
// @todo
total_geom_time = context_iterator.converter().total_geom_time;
total_map_time = context_iterator.converter().total_map_time;
*/
}
template <typename Fn>
void operator()(Fn fn) {
std::clock_t box_overlap_begin = std::clock();
CGAL::box_self_intersection_d(boxes.begin(), boxes.end(), [](Box& x, Box& y) {});
std::clock_t box_overlap_end = std::clock();
total_box_time += (box_overlap_end - box_overlap_begin) / (double) CLOCKS_PER_SEC;
CGAL::box_self_intersection_d(boxes.begin(), boxes.end(), fn);
}
};
#endif
+7 -7
View File
@@ -5,10 +5,10 @@ file(GLOB SERIALIZERS_H_FILES *.h)
file(GLOB SERIALIZERS_S_H_FILES schema_dependent/*.h) file(GLOB SERIALIZERS_S_H_FILES schema_dependent/*.h)
function(add_document_serializer_plugin target output_name) function(add_document_serializer_plugin target output_name)
cmake_parse_arguments(PLUGIN "" "" "SOURCES;LIBRARIES" ${ARGN}) cmake_parse_arguments(PLUGIN "" "" "SOURCES;LIBRARIES;DEFINITIONS" ${ARGN})
add_library(${target} SHARED ${PLUGIN_SOURCES}) add_library(${target} SHARED ${PLUGIN_SOURCES})
target_compile_definitions(${target} PRIVATE SERIALIZERS_EXPORTS BOOST_DLL_USE_STD_FS) target_compile_definitions(${target} PRIVATE SERIALIZERS_EXPORTS BOOST_DLL_USE_STD_FS ${PLUGIN_DEFINITIONS})
target_link_libraries(${target} PRIVATE plugin IfcGeom IfcParse ${PLUGIN_LIBRARIES}) target_link_libraries(${target} PRIVATE plugin IfcGeom IfcParse ${PLUGIN_LIBRARIES})
set_target_properties(${target} PROPERTIES set_target_properties(${target} PROPERTIES
OUTPUT_NAME "${output_name}" OUTPUT_NAME "${output_name}"
@@ -21,10 +21,10 @@ function(add_document_serializer_plugin target output_name)
endfunction() endfunction()
function(add_geometry_serializer_plugin target output_name) function(add_geometry_serializer_plugin target output_name)
cmake_parse_arguments(PLUGIN "" "" "SOURCES;LIBRARIES" ${ARGN}) cmake_parse_arguments(PLUGIN "" "" "SOURCES;LIBRARIES;DEFINITIONS" ${ARGN})
add_library(${target} SHARED ${PLUGIN_SOURCES}) add_library(${target} SHARED ${PLUGIN_SOURCES})
target_compile_definitions(${target} PRIVATE SERIALIZERS_EXPORTS BOOST_DLL_USE_STD_FS) target_compile_definitions(${target} PRIVATE SERIALIZERS_EXPORTS BOOST_DLL_USE_STD_FS ${PLUGIN_DEFINITIONS})
target_link_libraries(${target} PRIVATE plugin IfcGeom IfcParse ${PLUGIN_LIBRARIES}) target_link_libraries(${target} PRIVATE plugin IfcGeom IfcParse ${PLUGIN_LIBRARIES})
if(WITH_PROJ) if(WITH_PROJ)
find_package(proj REQUIRED) find_package(proj REQUIRED)
@@ -59,15 +59,15 @@ add_geometry_serializer_plugin(geometry_serializer_obj "geometry.obj" SOURCES ge
add_geometry_serializer_plugin(geometry_serializer_ttl "geometry.ttl" SOURCES geometry_ttl_plugin.cpp TtlWktSerializer.cpp LIBRARIES ${ttl_geometry_serializer_libraries}) add_geometry_serializer_plugin(geometry_serializer_ttl "geometry.ttl" SOURCES geometry_ttl_plugin.cpp TtlWktSerializer.cpp LIBRARIES ${ttl_geometry_serializer_libraries})
if(OPENCOLLADA_FOUND) if(OPENCOLLADA_FOUND)
add_geometry_serializer_plugin(geometry_serializer_dae "geometry.dae" SOURCES geometry_dae_plugin.cpp ColladaSerializer.cpp LIBRARIES ${OPENCOLLADA_LIBRARIES}) add_geometry_serializer_plugin(geometry_serializer_dae "geometry.dae" SOURCES geometry_dae_plugin.cpp ColladaSerializer.cpp LIBRARIES OpenCOLLADA::OpenCOLLADA DEFINITIONS WITH_OPENCOLLADA)
endif() endif()
if(GLTF_SUPPORT) if(GLTF_SUPPORT)
add_geometry_serializer_plugin(geometry_serializer_glb "geometry.glb" SOURCES geometry_glb_plugin.cpp GltfSerializer.cpp) add_geometry_serializer_plugin(geometry_serializer_glb "geometry.glb" SOURCES geometry_glb_plugin.cpp GltfSerializer.cpp LIBRARIES nlohmann_json::nlohmann_json DEFINITIONS WITH_GLTF)
endif() endif()
if(USD_SUPPORT) if(USD_SUPPORT)
add_geometry_serializer_plugin(geometry_serializer_usd "geometry.usd" SOURCES geometry_usd_plugin.cpp USDSerializer.cpp LIBRARIES ${USD_LIBRARIES}) add_geometry_serializer_plugin(geometry_serializer_usd "geometry.usd" SOURCES geometry_usd_plugin.cpp USDSerializer.cpp LIBRARIES pxr::USD)
endif() endif()
if(WITH_OPENCASCADE) if(WITH_OPENCASCADE)
+2
View File
@@ -51,6 +51,8 @@ plugin::metadata plugin_metadata() {
void register_plugin(document_serializer_registry& registry, const plugin::module& module) { void register_plugin(document_serializer_registry& registry, const plugin::module& module) {
document_serializer_info info; document_serializer_info info;
info.format = "rdb"; info.format = "rdb";
info.name = "RocksDB";
info.description = "RocksDB key-value store serialization of IFC data.";
info.supports_ifc_file = false; info.supports_ifc_file = false;
info.supports_input_filename = true; info.supports_input_filename = true;
info.writes_final_output = true; info.writes_final_output = true;
@@ -65,6 +65,12 @@ void ifcopenshell::serializers::document_serializer_registry::bind(const documen
entry entry; entry entry;
entry.info_ = info; entry.info_ = info;
entry.info_.format = document_serializer_key(entry.info_.format); entry.info_.format = document_serializer_key(entry.info_.format);
if (entry.info_.name.empty() && !entry.info_.format.empty()) {
entry.info_.name = boost::to_upper_copy(entry.info_.format);
}
if (entry.info_.description.empty()) {
entry.info_.description = entry.info_.name;
}
entry.info_.schema_name = document_serializer_schema_key(entry.info_.schema_name); entry.info_.schema_name = document_serializer_schema_key(entry.info_.schema_name);
entry.create_ = create; entry.create_ = create;
entry.module_ = module.meta().id.empty() ? plugin::module(document_serializer_plugin_metadata(entry.info_.format, entry.info_.schema_name)) : module; entry.module_ = module.meta().id.empty() ? plugin::module(document_serializer_plugin_metadata(entry.info_.format, entry.info_.schema_name)) : module;
@@ -38,6 +38,8 @@ namespace serializers {
struct SERIALIZERS_API document_serializer_info { struct SERIALIZERS_API document_serializer_info {
std::string format; std::string format;
std::string name;
std::string description;
std::string schema_name; std::string schema_name;
bool supports_ifc_file = true; bool supports_ifc_file = true;
bool supports_input_filename = false; bool supports_input_filename = false;
+2
View File
@@ -44,6 +44,8 @@ boost::shared_ptr<GeometrySerializer> create_serializer(const geometry_serialize
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) { void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
geometry_serializer_info info; geometry_serializer_info info;
info.format = "dae"; info.format = "dae";
info.name = "Collada";
info.description = "Digital Assets Exchange.";
info.extensions = { ".dae" }; info.extensions = { ".dae" };
info.supports_triangulation = true; info.supports_triangulation = true;
info.supports_user_element_hierarchy = true; info.supports_user_element_hierarchy = true;
+2
View File
@@ -44,6 +44,8 @@ boost::shared_ptr<GeometrySerializer> create_serializer(const geometry_serialize
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) { void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
geometry_serializer_info info; geometry_serializer_info info;
info.format = "glb"; info.format = "glb";
info.name = "glTF";
info.description = "Binary glTF v2.0.";
info.extensions = { ".glb" }; info.extensions = { ".glb" };
info.supports_triangulation = true; info.supports_triangulation = true;
info.supports_user_element_hierarchy = true; info.supports_user_element_hierarchy = true;
+2
View File
@@ -48,6 +48,8 @@ void configure_serializer(geometry_serializer_context& context) {
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) { void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
geometry_serializer_info info; geometry_serializer_info info;
info.format = "hdf"; info.format = "hdf";
info.name = "HDF";
info.description = "Hierarchical Data Format storing positions, normals and indices.";
info.extensions = { ".h5" }; info.extensions = { ".h5" };
info.kernel_ids = { "opencascade" }; info.kernel_ids = { "opencascade" };
info.supports_triangulation = true; info.supports_triangulation = true;
+2
View File
@@ -58,6 +58,8 @@ void configure_serializer(geometry_serializer_context& context) {
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) { void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
geometry_serializer_info info; geometry_serializer_info info;
info.format = "igs"; info.format = "igs";
info.name = "IGES";
info.description = "Initial Graphics Exchange Specification.";
info.extensions = { ".igs" }; info.extensions = { ".igs" };
info.kernel_ids = { "opencascade" }; info.kernel_ids = { "opencascade" };
info.supports_brep = true; info.supports_brep = true;
+2
View File
@@ -60,6 +60,8 @@ void configure_serializer(geometry_serializer_context& context) {
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) { void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
geometry_serializer_info info; geometry_serializer_info info;
info.format = "obj"; info.format = "obj";
info.name = "WaveFront OBJ";
info.description = "A .mtl file is also created.";
info.extensions = { ".obj" }; info.extensions = { ".obj" };
info.supports_triangulation = true; info.supports_triangulation = true;
registry.bind(info, create_serializer, configure_serializer, module); registry.bind(info, create_serializer, configure_serializer, module);
@@ -59,6 +59,12 @@ void ifcopenshell::serializers::geometry_serializer_registry::bind(const geometr
entry entry; entry entry;
entry.info_ = info; entry.info_ = info;
entry.info_.format = boost::to_lower_copy(entry.info_.format); entry.info_.format = boost::to_lower_copy(entry.info_.format);
if (entry.info_.name.empty() && !entry.info_.format.empty()) {
entry.info_.name = boost::to_upper_copy(entry.info_.format);
}
if (entry.info_.description.empty()) {
entry.info_.description = entry.info_.name;
}
entry.create_ = create; entry.create_ = create;
entry.configure_ = configure; entry.configure_ = configure;
entry.module_ = module.meta().id.empty() ? plugin::module(geometry_serializer_plugin_metadata(entry.info_.format)) : module; entry.module_ = module.meta().id.empty() ? plugin::module(geometry_serializer_plugin_metadata(entry.info_.format)) : module;
@@ -37,6 +37,8 @@ namespace serializers {
struct SERIALIZERS_API geometry_serializer_info { struct SERIALIZERS_API geometry_serializer_info {
std::string format; std::string format;
std::string name;
std::string description;
std::vector<std::string> extensions; std::vector<std::string> extensions;
std::vector<std::string> kernel_ids; std::vector<std::string> kernel_ids;
bool supports_triangulation = false; bool supports_triangulation = false;
+2
View File
@@ -49,6 +49,8 @@ void configure_serializer(geometry_serializer_context& context) {
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) { void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
geometry_serializer_info info; geometry_serializer_info info;
info.format = "stp"; info.format = "stp";
info.name = "STEP";
info.description = "Standard for the Exchange of Product Data.";
info.extensions = { ".stp" }; info.extensions = { ".stp" };
info.kernel_ids = { "opencascade" }; info.kernel_ids = { "opencascade" };
info.supports_brep = true; info.supports_brep = true;
+2
View File
@@ -49,6 +49,8 @@ void configure_serializer(geometry_serializer_context& context) {
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) { void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
geometry_serializer_info info; geometry_serializer_info info;
info.format = "svg"; info.format = "svg";
info.name = "SVG";
info.description = "Scalable Vector Graphics (2D floor plan).";
info.extensions = { ".svg" }; info.extensions = { ".svg" };
info.kernel_ids = { "opencascade" }; info.kernel_ids = { "opencascade" };
info.supports_brep = true; info.supports_brep = true;
+2
View File
@@ -49,6 +49,8 @@ void configure_serializer(geometry_serializer_context& context) {
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) { void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
geometry_serializer_info info; geometry_serializer_info info;
info.format = "ttl"; info.format = "ttl";
info.name = "TTL/WKT";
info.description = "RDF Turtle with Well-Known Text geometry.";
info.extensions = { ".ttl" }; info.extensions = { ".ttl" };
info.supports_triangulation = true; info.supports_triangulation = true;
info.supports_brep = true; info.supports_brep = true;
+2
View File
@@ -44,6 +44,8 @@ boost::shared_ptr<GeometrySerializer> create_serializer(const geometry_serialize
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) { void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
geometry_serializer_info info; geometry_serializer_info info;
info.format = "usd"; info.format = "usd";
info.name = "USD";
info.description = "Universal Scene Description.";
info.extensions = { ".usd", ".usda", ".usdc" }; info.extensions = { ".usd", ".usda", ".usdc" };
info.supports_triangulation = true; info.supports_triangulation = true;
info.supports_user_element_hierarchy = true; info.supports_user_element_hierarchy = true;
@@ -3,7 +3,7 @@ foreach(schema ${SCHEMA_VERSIONS})
target_compile_definitions(document_serializer_xml_ifc${schema} PRIVATE IfcSchema=Ifc${schema}) target_compile_definitions(document_serializer_xml_ifc${schema} PRIVATE IfcSchema=Ifc${schema})
if(GLTF_SUPPORT) if(GLTF_SUPPORT)
add_document_serializer_plugin(document_serializer_json_ifc${schema} "document.json.ifc${schema}" SOURCES JsonSerializer.cpp json_plugin.cpp) add_document_serializer_plugin(document_serializer_json_ifc${schema} "document.json.ifc${schema}" SOURCES JsonSerializer.cpp json_plugin.cpp LIBRARIES nlohmann_json::nlohmann_json DEFINITIONS WITH_GLTF)
target_compile_definitions(document_serializer_json_ifc${schema} PRIVATE IfcSchema=Ifc${schema}) target_compile_definitions(document_serializer_json_ifc${schema} PRIVATE IfcSchema=Ifc${schema})
endif() endif()
endforeach() endforeach()
@@ -53,6 +53,8 @@ plugin::metadata plugin_metadata() {
void register_plugin(document_serializer_registry& registry, const plugin::module& module) { void register_plugin(document_serializer_registry& registry, const plugin::module& module) {
document_serializer_info info; document_serializer_info info;
info.format = "json"; info.format = "json";
info.name = "JSON";
info.description = "Property definitions and decomposition tree in xeokit JSON format.";
info.schema_name = STRINGIFY(IfcSchema); info.schema_name = STRINGIFY(IfcSchema);
registry.bind(info, create_serializer, module); registry.bind(info, create_serializer, module);
} }
@@ -48,6 +48,8 @@ plugin::metadata plugin_metadata() {
void register_plugin(document_serializer_registry& registry, const plugin::module& module) { void register_plugin(document_serializer_registry& registry, const plugin::module& module) {
document_serializer_info info; document_serializer_info info;
info.format = "xml"; info.format = "xml";
info.name = "XML";
info.description = "Property definitions and decomposition tree.";
info.schema_name = STRINGIFY(IfcSchema); info.schema_name = STRINGIFY(IfcSchema);
registry.bind(info, create_serializer, module); registry.bind(info, create_serializer, module);
} }