mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
IfcConvert Plug-in discovery for info print
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
# IfcConvert
|
||||
add_executable(IfcConvert IfcConvert.cpp)
|
||||
|
||||
target_link_libraries(IfcConvert PRIVATE IfcGeom IfcParse ${OpenCASCADE_LIBRARIES} ${Boost_LIBRARIES} ${HDF5_LIBRARIES} ${USD_LIBRARIES})
|
||||
if(TARGET geometry_serializer_hdf)
|
||||
target_link_libraries(IfcConvert PRIVATE geometry_serializer_hdf)
|
||||
endif()
|
||||
target_link_libraries(IfcConvert PRIVATE IfcGeom IfcParse ${OpenCASCADE_LIBRARIES} ${Boost_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})
|
||||
endif()
|
||||
|
||||
+115
-41
@@ -50,11 +50,13 @@
|
||||
#include <boost/optional/optional_io.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <time.h>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <time.h>
|
||||
|
||||
#if USE_VLD
|
||||
#include <vld.h>
|
||||
@@ -85,6 +87,91 @@ const std::string TEMP_FILE_EXTENSION = ".tmp";
|
||||
|
||||
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()
|
||||
{
|
||||
cout_ << "IfcOpenShell IfcConvert " << IFCOPENSHELL_VERSION;
|
||||
@@ -98,31 +185,11 @@ void print_usage(bool suggest_help = true)
|
||||
{
|
||||
cout_ << "Usage: IfcConvert [options] <input.ifc> [<output>]\n"
|
||||
<< "\n"
|
||||
<< "Converts (the geometry in) an IFC file into one of the following formats:\n"
|
||||
<< " .obj WaveFront OBJ (a .mtl file is also created)\n"
|
||||
#ifdef WITH_OPENCOLLADA
|
||||
<< " .dae Collada Digital Assets Exchange\n"
|
||||
#endif
|
||||
#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"
|
||||
<< "Converts (the geometry in) an IFC file into one of the following formats:\n\n";
|
||||
print_serializer_section("Geometry serializers:", geometry_serializer_usage_lines());
|
||||
print_serializer_section("Document serializers:", document_serializer_usage_lines());
|
||||
print_serializer_section("Built-in:", { { ".ifc", "IFC-SPF", "Industry Foundation Classes." } });
|
||||
cout_
|
||||
<< "If no output filename given, <input>" << ifcopenshell::path::from_utf8(DEFAULT_EXTENSION) << " will be used as the output file.\n";
|
||||
if (suggest_help) {
|
||||
cout_ << "\nRun 'IfcConvert --help' for more information.";
|
||||
@@ -224,6 +291,13 @@ int main(int argc, char** argv) {
|
||||
path_t cache_file;
|
||||
std::string log_format;
|
||||
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");
|
||||
verbosity_counter vcounter;
|
||||
@@ -232,14 +306,15 @@ int main(int argc, char** argv) {
|
||||
("version", "display version information")
|
||||
("verbose,v", po::value(&vcounter)->zero_tokens(), "more verbose log messages. Use twice (-vv) for debugging level.")
|
||||
("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")
|
||||
("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")
|
||||
("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");
|
||||
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;
|
||||
fileio_options.add_options()
|
||||
@@ -248,11 +323,12 @@ int main(int argc, char** argv) {
|
||||
#endif
|
||||
("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")
|
||||
#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)")
|
||||
;
|
||||
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");
|
||||
ifc_options.add_options()
|
||||
@@ -534,11 +610,8 @@ int main(int argc, char** argv) {
|
||||
|
||||
boost::optional<std::list<IfcGeom::Element*>> elems_from_adaptor;
|
||||
|
||||
const path_t CACHE = ifcopenshell::path::from_utf8(".cache"),
|
||||
HDF = ifcopenshell::path::from_utf8(".h5"),
|
||||
IFC = ifcopenshell::path::from_utf8(".ifc");
|
||||
const path_t 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);
|
||||
if (document_serializer_info) {
|
||||
int exit_code = EXIT_FAILURE;
|
||||
@@ -613,7 +686,6 @@ int main(int argc, char** argv) {
|
||||
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);
|
||||
if (!geometry_serializer_info) {
|
||||
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));
|
||||
}
|
||||
|
||||
#if defined(WITH_HDF5) && defined(IFOPSH_WITH_OPENCASCADE)
|
||||
#ifdef WITH_HDF5
|
||||
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) {
|
||||
const path_t CACHE = ifcopenshell::path::from_utf8(".cache");
|
||||
const path_t HDF = ifcopenshell::path::from_utf8(".h5");
|
||||
if (!vmap.count("cache-file")) {
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -5,10 +5,10 @@ file(GLOB SERIALIZERS_H_FILES *.h)
|
||||
file(GLOB SERIALIZERS_S_H_FILES schema_dependent/*.h)
|
||||
|
||||
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})
|
||||
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})
|
||||
set_target_properties(${target} PROPERTIES
|
||||
OUTPUT_NAME "${output_name}"
|
||||
@@ -21,10 +21,10 @@ function(add_document_serializer_plugin target output_name)
|
||||
endfunction()
|
||||
|
||||
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})
|
||||
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})
|
||||
if(WITH_PROJ)
|
||||
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})
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
if(WITH_OPENCASCADE)
|
||||
|
||||
@@ -51,6 +51,8 @@ plugin::metadata plugin_metadata() {
|
||||
void register_plugin(document_serializer_registry& registry, const plugin::module& module) {
|
||||
document_serializer_info info;
|
||||
info.format = "rdb";
|
||||
info.name = "RocksDB";
|
||||
info.description = "RocksDB key-value store serialization of IFC data.";
|
||||
info.supports_ifc_file = false;
|
||||
info.supports_input_filename = true;
|
||||
info.writes_final_output = true;
|
||||
|
||||
@@ -65,6 +65,12 @@ void ifcopenshell::serializers::document_serializer_registry::bind(const documen
|
||||
entry entry;
|
||||
entry.info_ = info;
|
||||
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.create_ = create;
|
||||
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 {
|
||||
std::string format;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string schema_name;
|
||||
bool supports_ifc_file = true;
|
||||
bool supports_input_filename = false;
|
||||
|
||||
@@ -44,6 +44,8 @@ boost::shared_ptr<GeometrySerializer> create_serializer(const geometry_serialize
|
||||
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
|
||||
geometry_serializer_info info;
|
||||
info.format = "dae";
|
||||
info.name = "Collada";
|
||||
info.description = "Digital Assets Exchange.";
|
||||
info.extensions = { ".dae" };
|
||||
info.supports_triangulation = true;
|
||||
info.supports_user_element_hierarchy = true;
|
||||
|
||||
@@ -44,6 +44,8 @@ boost::shared_ptr<GeometrySerializer> create_serializer(const geometry_serialize
|
||||
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
|
||||
geometry_serializer_info info;
|
||||
info.format = "glb";
|
||||
info.name = "glTF";
|
||||
info.description = "Binary glTF v2.0.";
|
||||
info.extensions = { ".glb" };
|
||||
info.supports_triangulation = true;
|
||||
info.supports_user_element_hierarchy = true;
|
||||
|
||||
@@ -48,6 +48,8 @@ void configure_serializer(geometry_serializer_context& context) {
|
||||
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
|
||||
geometry_serializer_info info;
|
||||
info.format = "hdf";
|
||||
info.name = "HDF";
|
||||
info.description = "Hierarchical Data Format storing positions, normals and indices.";
|
||||
info.extensions = { ".h5" };
|
||||
info.kernel_ids = { "opencascade" };
|
||||
info.supports_triangulation = true;
|
||||
|
||||
@@ -58,6 +58,8 @@ void configure_serializer(geometry_serializer_context& context) {
|
||||
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
|
||||
geometry_serializer_info info;
|
||||
info.format = "igs";
|
||||
info.name = "IGES";
|
||||
info.description = "Initial Graphics Exchange Specification.";
|
||||
info.extensions = { ".igs" };
|
||||
info.kernel_ids = { "opencascade" };
|
||||
info.supports_brep = true;
|
||||
|
||||
@@ -60,6 +60,8 @@ void configure_serializer(geometry_serializer_context& context) {
|
||||
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
|
||||
geometry_serializer_info info;
|
||||
info.format = "obj";
|
||||
info.name = "WaveFront OBJ";
|
||||
info.description = "A .mtl file is also created.";
|
||||
info.extensions = { ".obj" };
|
||||
info.supports_triangulation = true;
|
||||
registry.bind(info, create_serializer, configure_serializer, module);
|
||||
|
||||
@@ -59,6 +59,12 @@ void ifcopenshell::serializers::geometry_serializer_registry::bind(const geometr
|
||||
entry entry;
|
||||
entry.info_ = info;
|
||||
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.configure_ = configure;
|
||||
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 {
|
||||
std::string format;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::vector<std::string> extensions;
|
||||
std::vector<std::string> kernel_ids;
|
||||
bool supports_triangulation = false;
|
||||
|
||||
@@ -49,6 +49,8 @@ void configure_serializer(geometry_serializer_context& context) {
|
||||
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
|
||||
geometry_serializer_info info;
|
||||
info.format = "stp";
|
||||
info.name = "STEP";
|
||||
info.description = "Standard for the Exchange of Product Data.";
|
||||
info.extensions = { ".stp" };
|
||||
info.kernel_ids = { "opencascade" };
|
||||
info.supports_brep = true;
|
||||
|
||||
@@ -49,6 +49,8 @@ void configure_serializer(geometry_serializer_context& context) {
|
||||
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
|
||||
geometry_serializer_info info;
|
||||
info.format = "svg";
|
||||
info.name = "SVG";
|
||||
info.description = "Scalable Vector Graphics (2D floor plan).";
|
||||
info.extensions = { ".svg" };
|
||||
info.kernel_ids = { "opencascade" };
|
||||
info.supports_brep = true;
|
||||
|
||||
@@ -49,6 +49,8 @@ void configure_serializer(geometry_serializer_context& context) {
|
||||
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
|
||||
geometry_serializer_info info;
|
||||
info.format = "ttl";
|
||||
info.name = "TTL/WKT";
|
||||
info.description = "RDF Turtle with Well-Known Text geometry.";
|
||||
info.extensions = { ".ttl" };
|
||||
info.supports_triangulation = true;
|
||||
info.supports_brep = true;
|
||||
|
||||
@@ -44,6 +44,8 @@ boost::shared_ptr<GeometrySerializer> create_serializer(const geometry_serialize
|
||||
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
|
||||
geometry_serializer_info info;
|
||||
info.format = "usd";
|
||||
info.name = "USD";
|
||||
info.description = "Universal Scene Description.";
|
||||
info.extensions = { ".usd", ".usda", ".usdc" };
|
||||
info.supports_triangulation = 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})
|
||||
|
||||
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})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
@@ -53,6 +53,8 @@ plugin::metadata plugin_metadata() {
|
||||
void register_plugin(document_serializer_registry& registry, const plugin::module& module) {
|
||||
document_serializer_info info;
|
||||
info.format = "json";
|
||||
info.name = "JSON";
|
||||
info.description = "Property definitions and decomposition tree in xeokit JSON format.";
|
||||
info.schema_name = STRINGIFY(IfcSchema);
|
||||
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) {
|
||||
document_serializer_info info;
|
||||
info.format = "xml";
|
||||
info.name = "XML";
|
||||
info.description = "Property definitions and decomposition tree.";
|
||||
info.schema_name = STRINGIFY(IfcSchema);
|
||||
registry.bind(info, create_serializer, module);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user