mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
+10
-1
@@ -268,6 +268,10 @@ endif()
|
||||
# resolved. Also add thread and rt libraries.
|
||||
get_filename_component(libTKernelExt ${libTKernel} EXT)
|
||||
if("${libTKernelExt}" STREQUAL ".a")
|
||||
set(OCCT_STATIC ON)
|
||||
endif()
|
||||
|
||||
if(OCCT_STATIC)
|
||||
find_package(Threads)
|
||||
# OPENCASCADE_LIBRARIES repeated three times below in order to fix cyclic dependencies - use --start-group ... --end-group instead?
|
||||
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
@@ -563,7 +567,12 @@ set(SCHEMA_AGNOSTIC_FILES ${SCHEMA_AGNOSTIC_H_FILES} ${SCHEMA_AGNOSTIC_CPP_FILES
|
||||
|
||||
add_library(IfcGeom ${SCHEMA_AGNOSTIC_FILES})
|
||||
set_target_properties(IfcGeom PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS)
|
||||
TARGET_LINK_LIBRARIES(IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
|
||||
|
||||
if (UNIX)
|
||||
find_package(Threads)
|
||||
endif()
|
||||
|
||||
TARGET_LINK_LIBRARIES(IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
endif(BUILD_IFCGEOM)
|
||||
|
||||
|
||||
@@ -62,8 +62,9 @@
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
// C++11 header:
|
||||
|
||||
#include <random>
|
||||
#include <thread>
|
||||
|
||||
#if defined(_MSC_VER) && defined(_UNICODE)
|
||||
typedef std::wstring path_t;
|
||||
@@ -218,9 +219,13 @@ int main(int argc, char** argv) {
|
||||
ifc_options.add_options()
|
||||
("calculate-quantities", "Calculate or fix the physical quantity definitions "
|
||||
"based on an interpretation of the geometry when exporting IFC");
|
||||
|
||||
int num_threads;
|
||||
|
||||
po::options_description geom_options("Geometry options");
|
||||
geom_options.add_options()
|
||||
("threads,j", po::value<int>(&num_threads)->default_value(1),
|
||||
"Number of parallel processing threads for geometry interpretation.")
|
||||
("plan",
|
||||
"Specifies whether to include curves in the output result. Typically "
|
||||
"these are representations of type Plan or Axis. Excluded by default.")
|
||||
@@ -516,7 +521,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
Logger::SetOutput(&cout_, &log_stream);
|
||||
Logger::SetOutput(quiet ? nullptr : &cout_, &log_stream);
|
||||
Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR);
|
||||
|
||||
path_t output_temp_filename = output_filename + IfcUtil::path::from_utf8(TEMP_FILE_EXTENSION);
|
||||
@@ -730,7 +735,18 @@ int main(int argc, char** argv) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
IfcGeom::Iterator<real_t> context_iterator(settings, ifc_file, filter_funcs);
|
||||
if (num_threads <= 0) {
|
||||
num_threads = std::thread::hardware_concurrency();
|
||||
Logger::Notice("Using " + std::to_string(num_threads) + " threads");
|
||||
}
|
||||
|
||||
if (!quiet && num_threads > 1) {
|
||||
Logger::Status("Creating geometry...");
|
||||
}
|
||||
|
||||
Logger::SetOutput(quiet ? nullptr : &cout_, &log_stream);
|
||||
|
||||
IfcGeom::Iterator<real_t> context_iterator(settings, ifc_file, filter_funcs, num_threads);
|
||||
if (!context_iterator.initialize()) {
|
||||
/// @todo It would be nice to know and print separate error prints for a case where we found no entities
|
||||
/// and for a case we found no entities that satisfy our filtering criteria.
|
||||
@@ -784,7 +800,11 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
Logger::Status("Creating geometry...");
|
||||
if (num_threads == 1) {
|
||||
Logger::Status("Creating geometry...");
|
||||
} else {
|
||||
Logger::Status("Writing geometry...");
|
||||
}
|
||||
}
|
||||
|
||||
// The functions IfcGeom::Iterator::get() and IfcGeom::Iterator::next()
|
||||
@@ -815,13 +835,13 @@ int main(int argc, char** argv) {
|
||||
if (quiet) {
|
||||
const int progress = context_iterator.progress();
|
||||
for (; old_progress < progress; ++old_progress) {
|
||||
std::cout << ".";
|
||||
cout_ << ".";
|
||||
if (stderr_progress)
|
||||
std::cerr << ".";
|
||||
cerr_ << ".";
|
||||
}
|
||||
std::cout << std::flush;
|
||||
cout_ << std::flush;
|
||||
if (stderr_progress)
|
||||
std::cerr << std::flush;
|
||||
cerr_ << std::flush;
|
||||
} else {
|
||||
const int progress = context_iterator.progress() / 2;
|
||||
if (old_progress != progress) Logger::ProgressBar(progress);
|
||||
@@ -832,15 +852,17 @@ int main(int argc, char** argv) {
|
||||
|
||||
if (!no_progress && quiet) {
|
||||
for (; old_progress < 100; ++old_progress) {
|
||||
std::cout << ".";
|
||||
cout_ << ".";
|
||||
if (stderr_progress)
|
||||
std::cerr << ".";
|
||||
cerr_ << ".";
|
||||
}
|
||||
cout_ << std::flush;
|
||||
if (stderr_progress) {
|
||||
cerr_ << std::flush;
|
||||
}
|
||||
std::cout << std::flush;
|
||||
if (stderr_progress)
|
||||
std::cerr << std::flush;
|
||||
} else {
|
||||
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(num_created) +
|
||||
const std::string task = ((num_threads == 1) ? "creating" : "writing");
|
||||
Logger::Status("\rDone " + task + " geometry (" + boost::lexical_cast<std::string>(num_created) +
|
||||
" objects) ");
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace IfcGeom {
|
||||
namespace {
|
||||
template <typename P, typename PP>
|
||||
struct MAKE_TYPE_NAME(factory_t) {
|
||||
IfcGeom::IteratorImplementation<P, PP>* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters) const {
|
||||
return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)<P, PP>(settings, file, filters);
|
||||
IfcGeom::IteratorImplementation<P, PP>* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, int num_threads) const {
|
||||
return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)<P, PP>(settings, file, filters, num_threads);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -63,6 +63,11 @@
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
@@ -84,6 +89,8 @@
|
||||
#include "../ifcgeom_schema_agnostic/IfcGeomFilter.h"
|
||||
#include "../ifcgeom_schema_agnostic/IteratorImplementation.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
// The infamous min & max Win32 #defines can leak here from OCE depending on the build configuration
|
||||
#ifdef min
|
||||
#undef min
|
||||
@@ -92,12 +99,93 @@
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
template <typename P, typename PP=P>
|
||||
struct geometry_conversion_task {
|
||||
int index;
|
||||
IfcSchema::IfcRepresentation *representation;
|
||||
IfcSchema::IfcProduct::list::ptr products;
|
||||
std::vector<IfcGeom::BRepElement<P, PP>*> breps;
|
||||
std::vector<IfcGeom::Element<P, PP>*> elements;
|
||||
};
|
||||
|
||||
template <typename P, typename PP=P>
|
||||
IfcGeom::Element<P, PP>* process_based_on_settings(
|
||||
const IfcGeom::IteratorSettings& settings,
|
||||
IfcGeom::BRepElement<P, PP>* elem,
|
||||
IfcGeom::TriangulationElement<P, PP>* previous=nullptr)
|
||||
{
|
||||
if (settings.get(IfcGeom::IteratorSettings::USE_BREP_DATA)) {
|
||||
try {
|
||||
return new IfcGeom::SerializedElement<P, PP>(*elem);
|
||||
} catch (...) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed.");
|
||||
return nullptr;
|
||||
}
|
||||
} else if (!settings.get(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION)) {
|
||||
try {
|
||||
if (!previous) {
|
||||
return new IfcGeom::TriangulationElement<P, PP>(*elem);
|
||||
} else {
|
||||
return new IfcGeom::TriangulationElement<P, PP>(*elem, previous->geometry_pointer());
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed.");
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename P, typename PP = P>
|
||||
void create_element(
|
||||
IfcGeom::MAKE_TYPE_NAME(Kernel)* kernel,
|
||||
const IfcGeom::IteratorSettings& settings,
|
||||
geometry_conversion_task<P, PP>* rep)
|
||||
{
|
||||
IfcSchema::IfcRepresentation *representation = rep->representation;
|
||||
IfcSchema::IfcProduct *product = *rep->products->begin();
|
||||
auto brep = kernel->create_brep_for_representation_and_product<P, PP>(settings, representation, product);
|
||||
if (!brep) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto elem = process_based_on_settings(settings, brep);
|
||||
if (!elem) {
|
||||
return;
|
||||
}
|
||||
|
||||
rep->breps = { brep };
|
||||
rep->elements = { elem };
|
||||
|
||||
for (auto it = rep->products->begin() + 1; it != rep->products->end(); ++it) {
|
||||
auto brep2 = kernel->create_brep_for_processed_representation<P, PP>(settings, representation, *it, brep);
|
||||
if (brep2) {
|
||||
auto elem2 = process_based_on_settings(settings, brep, dynamic_cast<IfcGeom::TriangulationElement<P, PP>*>(elem));
|
||||
if (elem2) {
|
||||
rep->breps.push_back(brep2);
|
||||
rep->elements.push_back(elem2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace IfcGeom {
|
||||
|
||||
template <typename P, typename PP>
|
||||
class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation<P, PP> {
|
||||
private:
|
||||
|
||||
int num_threads_;
|
||||
std::atomic<int> progress_;
|
||||
std::vector<geometry_conversion_task<P, PP>> tasks_;
|
||||
std::vector<IfcGeom::Element<P, PP>*> all_processed_elements_;
|
||||
std::vector<IfcGeom::BRepElement<P, PP>*> all_processed_native_elements_;
|
||||
typename std::vector<IfcGeom::Element<P, PP>*>::const_iterator task_result_iterator_;
|
||||
typename std::vector<IfcGeom::BRepElement<P, PP>*>::const_iterator native_task_result_iterator_;
|
||||
|
||||
MAKE_TYPE_NAME(IteratorImplementation_)(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I
|
||||
MAKE_TYPE_NAME(IteratorImplementation_)& operator=(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I
|
||||
|
||||
@@ -282,16 +370,118 @@ namespace IfcGeom {
|
||||
representation_iterator = representations->begin();
|
||||
ifcproducts.reset();
|
||||
|
||||
if (!create()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
done = 0;
|
||||
total = representations->size();
|
||||
|
||||
if (num_threads_ != 1) {
|
||||
collect();
|
||||
process_concurrently();
|
||||
} else {
|
||||
if (!create()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void collect() {
|
||||
int i = 0;
|
||||
IfcSchema::IfcProduct::list* previous = nullptr;
|
||||
while (auto rp = get_next_task()) {
|
||||
// Note that get_next_task() mutates the state of the iterator
|
||||
// we use that capture all products that can be processed as
|
||||
// part of this representation and then keep iterating until
|
||||
// the underlying list of products changes.
|
||||
if (ifcproducts.get() != previous) {
|
||||
previous = ifcproducts.get();
|
||||
geometry_conversion_task<P, PP> t;
|
||||
t.index = i++;
|
||||
t.representation = *representation_iterator;
|
||||
t.products = ifcproducts;
|
||||
tasks_.emplace_back(t);
|
||||
}
|
||||
|
||||
_nextShape();
|
||||
}
|
||||
}
|
||||
|
||||
void process_concurrently() {
|
||||
size_t conc_threads = num_threads_;
|
||||
if (conc_threads > tasks_.size()) {
|
||||
conc_threads = tasks_.size();
|
||||
}
|
||||
|
||||
std::vector<MAKE_TYPE_NAME(Kernel)*> kernel_pool;
|
||||
kernel_pool.reserve(conc_threads);
|
||||
for (unsigned i = 0; i < conc_threads; ++i) {
|
||||
kernel_pool.push_back(new MAKE_TYPE_NAME(Kernel)(kernel));
|
||||
}
|
||||
|
||||
std::vector<std::future<void>> threadpool;
|
||||
|
||||
int old_progress = -1;
|
||||
int processed = 0;
|
||||
|
||||
Logger::ProgressBar(0);
|
||||
|
||||
for (auto& rep : tasks_) {
|
||||
MAKE_TYPE_NAME(Kernel)* K = nullptr;
|
||||
if (threadpool.size() < kernel_pool.size()) {
|
||||
K = kernel_pool[threadpool.size()];
|
||||
}
|
||||
|
||||
while (threadpool.size() == conc_threads) {
|
||||
for (int i = 0; i < (int)threadpool.size(); i++) {
|
||||
std::future<void> &fu = threadpool[i];
|
||||
std::future_status status;
|
||||
status = fu.wait_for(std::chrono::seconds(0));
|
||||
if (status == std::future_status::ready) {
|
||||
fu.get();
|
||||
|
||||
processed += 1;
|
||||
progress_ = processed * 50 / tasks_.size();
|
||||
if (progress_ != old_progress) {
|
||||
Logger::ProgressBar(progress_);
|
||||
old_progress = progress_;
|
||||
}
|
||||
|
||||
std::swap(threadpool[i], threadpool.back());
|
||||
threadpool.pop_back();
|
||||
std::swap(kernel_pool[i], kernel_pool.back());
|
||||
K = kernel_pool.back();
|
||||
break;
|
||||
} // if
|
||||
} // for
|
||||
} // while
|
||||
|
||||
std::future<void> fu = std::async(std::launch::async, create_element<P, PP>, K, std::ref(settings), &rep);
|
||||
threadpool.emplace_back(std::move(fu));
|
||||
}
|
||||
|
||||
for (std::future<void> &fu : threadpool) {
|
||||
fu.get();
|
||||
|
||||
processed += 1;
|
||||
progress_ = processed * 50 / tasks_.size();
|
||||
if (progress_ != old_progress) {
|
||||
Logger::ProgressBar(progress_);
|
||||
old_progress = progress_;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& rep : tasks_) {
|
||||
all_processed_elements_.insert(all_processed_elements_.end(), rep.elements.begin(), rep.elements.end());
|
||||
all_processed_native_elements_.insert(all_processed_native_elements_.end(), rep.breps.begin(), rep.breps.end());
|
||||
}
|
||||
|
||||
task_result_iterator_ = all_processed_elements_.begin();
|
||||
native_task_result_iterator_ = all_processed_native_elements_.begin();
|
||||
|
||||
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(all_processed_elements_.size()) +
|
||||
" objects) ");
|
||||
}
|
||||
|
||||
/// Computes model's bounding box (bounds_min and bounds_max).
|
||||
/// @note Can take several minutes for large files.
|
||||
void compute_bounds()
|
||||
@@ -332,7 +522,13 @@ namespace IfcGeom {
|
||||
}
|
||||
}
|
||||
|
||||
int progress() const { return 100 * done / total; }
|
||||
int progress() const {
|
||||
if (num_threads_ == 1) {
|
||||
return 100 * done / total;
|
||||
} else {
|
||||
return progress_;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& getUnitName() const { return unit_name; }
|
||||
|
||||
@@ -403,13 +599,13 @@ namespace IfcGeom {
|
||||
return associated_single_materials.size() == 1;
|
||||
}
|
||||
|
||||
BRepElement<P, PP>* create_shape_model_for_next_entity() {
|
||||
boost::optional<std::pair<IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*>> get_next_task() {
|
||||
for (;;) {
|
||||
IfcSchema::IfcRepresentation* representation;
|
||||
|
||||
if ( representation_iterator == representations->end() ) {
|
||||
if (representation_iterator == representations->end()) {
|
||||
representations.reset();
|
||||
return 0; // reached the end of our list of representations
|
||||
return boost::none; // reached the end of our list of representations
|
||||
}
|
||||
representation = *representation_iterator;
|
||||
|
||||
@@ -417,20 +613,20 @@ namespace IfcGeom {
|
||||
// Init. the list of filtered IfcProducts for this representation
|
||||
ifcproducts = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list);
|
||||
IfcSchema::IfcProduct::list::ptr unfiltered_products = kernel.products_represented_by(representation);
|
||||
// Include only the desired products for processing.
|
||||
for (IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); jt != unfiltered_products->end(); ++jt) {
|
||||
IfcSchema::IfcProduct* prod = *jt;
|
||||
if (boost::all(filters_, filter_match(prod))) {
|
||||
ifcproducts->push(prod);
|
||||
}
|
||||
}
|
||||
// Include only the desired products for processing.
|
||||
for (IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); jt != unfiltered_products->end(); ++jt) {
|
||||
IfcSchema::IfcProduct* prod = *jt;
|
||||
if (boost::all(filters_, filter_match(prod))) {
|
||||
ifcproducts->push(prod);
|
||||
}
|
||||
}
|
||||
|
||||
if (ifcproducts->size() == 0) {
|
||||
_nextShape();
|
||||
continue;
|
||||
}
|
||||
if (ifcproducts->size() == 0) {
|
||||
_nextShape();
|
||||
continue;
|
||||
}
|
||||
|
||||
geometry_reuse_ok_for_current_representation_ = reuse_ok_(ifcproducts);
|
||||
geometry_reuse_ok_for_current_representation_ = reuse_ok_(ifcproducts);
|
||||
|
||||
IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap();
|
||||
|
||||
@@ -450,14 +646,14 @@ namespace IfcGeom {
|
||||
|
||||
// Check if this represenation has (or will be) processed as part its mapped representation
|
||||
bool representation_processed_as_mapped_item = false;
|
||||
IfcSchema::IfcRepresentation* representation_mapped_to = kernel.representation_mapped_to(representation);
|
||||
IfcSchema::IfcRepresentation* representation_mapped_to = kernel.representation_mapped_to(representation);
|
||||
if (representation_mapped_to) {
|
||||
representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ && (
|
||||
ok_mapped_representations->contains(representation_mapped_to) || reuse_ok_(kernel.products_represented_by(representation_mapped_to)));
|
||||
representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ && (
|
||||
ok_mapped_representations->contains(representation_mapped_to) || reuse_ok_(kernel.products_represented_by(representation_mapped_to)));
|
||||
}
|
||||
|
||||
if (representation_processed_as_mapped_item) {
|
||||
ok_mapped_representations->push(representation_mapped_to);
|
||||
ok_mapped_representations->push(representation_mapped_to);
|
||||
_nextShape();
|
||||
continue;
|
||||
}
|
||||
@@ -466,13 +662,28 @@ namespace IfcGeom {
|
||||
}
|
||||
|
||||
// Have we reached the end of our list of IfcProducts?
|
||||
if ( ifcproduct_iterator == ifcproducts->end() ) {
|
||||
if (ifcproduct_iterator == ifcproducts->end()) {
|
||||
_nextShape();
|
||||
continue;
|
||||
}
|
||||
|
||||
IfcSchema::IfcProduct* product = *ifcproduct_iterator;
|
||||
Logger::SetProduct(product);
|
||||
|
||||
|
||||
return std::make_pair(representation, product);
|
||||
}
|
||||
}
|
||||
|
||||
BRepElement<P, PP>* create_shape_model_for_next_entity() {
|
||||
for (;;) {
|
||||
auto rp = get_next_task();
|
||||
if (!rp) {
|
||||
return nullptr;
|
||||
}
|
||||
auto representation = rp->first;
|
||||
auto product = rp->second;
|
||||
|
||||
Logger::SetProduct(product);
|
||||
|
||||
BRepElement<P, PP>* element;
|
||||
if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) {
|
||||
@@ -520,13 +731,23 @@ namespace IfcGeom {
|
||||
/// Moves to the next shape representation, create its geometry, and returns the associated product.
|
||||
/// Use get() to retrieve the created geometry.
|
||||
IfcUtil::IfcBaseClass* next() {
|
||||
// Increment the iterator over the list of products using the current
|
||||
// shape representation
|
||||
if (ifcproducts) {
|
||||
++ifcproduct_iterator;
|
||||
}
|
||||
if (num_threads_ != 1) {
|
||||
task_result_iterator_++;
|
||||
native_task_result_iterator_++;
|
||||
if (task_result_iterator_ == all_processed_elements_.end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return (*task_result_iterator_)->product();
|
||||
}
|
||||
} else {
|
||||
// Increment the iterator over the list of products using the current
|
||||
// shape representation
|
||||
if (ifcproducts) {
|
||||
++ifcproduct_iterator;
|
||||
}
|
||||
|
||||
return create();
|
||||
return create();
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the representation of the current geometrical entity.
|
||||
@@ -534,9 +755,18 @@ namespace IfcGeom {
|
||||
{
|
||||
// TODO: Test settings and throw
|
||||
Element<P, PP>* ret = 0;
|
||||
if (current_triangulation) { ret = current_triangulation; }
|
||||
else if (current_serialization) { ret = current_serialization; }
|
||||
else if (current_shape_model) { ret = current_shape_model; }
|
||||
|
||||
if (num_threads_ != 1) {
|
||||
ret = *task_result_iterator_;
|
||||
} else {
|
||||
if (current_triangulation) {
|
||||
ret = current_triangulation;
|
||||
} else if (current_serialization) {
|
||||
ret = current_serialization;
|
||||
} else if (current_shape_model) {
|
||||
ret = current_shape_model;
|
||||
}
|
||||
}
|
||||
|
||||
// If we want to organize the element considering their hierarchy
|
||||
if (settings.get(IteratorSettings::SEARCH_FLOOR))
|
||||
@@ -591,7 +821,11 @@ namespace IfcGeom {
|
||||
BRepElement<P, PP>* get_native()
|
||||
{
|
||||
// TODO: Test settings and throw
|
||||
return current_shape_model;
|
||||
if (num_threads_ != 1) {
|
||||
return *native_task_result_iterator_;
|
||||
} else {
|
||||
return current_shape_model;
|
||||
}
|
||||
}
|
||||
|
||||
const Element<P, PP>* get_object(int id) {
|
||||
@@ -721,11 +955,12 @@ namespace IfcGeom {
|
||||
|
||||
bool owns_ifc_file;
|
||||
public:
|
||||
MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters)
|
||||
MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, int num_threads)
|
||||
: settings(settings)
|
||||
, ifc_file(file)
|
||||
, filters_(filters)
|
||||
, owns_ifc_file(false)
|
||||
, num_threads_(num_threads)
|
||||
{
|
||||
_initialize();
|
||||
}
|
||||
@@ -735,6 +970,16 @@ namespace IfcGeom {
|
||||
delete ifc_file;
|
||||
}
|
||||
|
||||
if (settings.get(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION)) {
|
||||
for (auto& p : all_processed_native_elements_) {
|
||||
delete p;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& p : all_processed_elements_) {
|
||||
delete p;
|
||||
}
|
||||
|
||||
free_shapes();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -83,19 +83,19 @@ namespace IfcGeom {
|
||||
IteratorImplementation<P, PP>* implementation_;
|
||||
|
||||
public:
|
||||
Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file)
|
||||
Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, int num_threads = 1)
|
||||
: file_(file)
|
||||
, settings_(settings)
|
||||
{
|
||||
implementation_ = iterator_implementations<P, PP>().construct(file_->schema()->name(), settings, file, filters_);
|
||||
implementation_ = iterator_implementations<P, PP>().construct(file_->schema()->name(), settings, file, filters_, num_threads);
|
||||
}
|
||||
|
||||
Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters)
|
||||
Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, size_t num_threads = 1)
|
||||
: file_(file)
|
||||
, settings_(settings)
|
||||
, filters_(filters)
|
||||
{
|
||||
implementation_ = iterator_implementations<P, PP>().construct(file_->schema()->name(), settings, file, filters_);
|
||||
implementation_ = iterator_implementations<P, PP>().construct(file_->schema()->name(), settings, file, filters_, num_threads);
|
||||
}
|
||||
|
||||
bool initialize() {
|
||||
|
||||
@@ -31,14 +31,14 @@ void IteratorFactoryImplementation<P, PP>::bind(const std::string& schema_name,
|
||||
}
|
||||
|
||||
template <typename P, typename PP>
|
||||
IfcGeom::IteratorImplementation<P, PP>* IteratorFactoryImplementation<P, PP>::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters) {
|
||||
IfcGeom::IteratorImplementation<P, PP>* IteratorFactoryImplementation<P, PP>::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, int num_threads) {
|
||||
const std::string schema_name_lower = boost::to_lower_copy(schema_name);
|
||||
typename std::map<std::string, typename get_factory_type<P, PP>::type>::const_iterator it;
|
||||
it = this->find(schema_name_lower);
|
||||
if (it == this->end()) {
|
||||
throw IfcParse::IfcException("No geometry iterator registered for " + schema_name);
|
||||
}
|
||||
return it->second(settings, file, filters);
|
||||
return it->second(settings, file, filters, num_threads);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ namespace IfcGeom {
|
||||
class BRepElement;
|
||||
}
|
||||
|
||||
typedef boost::function3<IfcGeom::IteratorImplementation<float, float>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&> iterator_float_float_fn;
|
||||
typedef boost::function3<IfcGeom::IteratorImplementation<float, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&> iterator_float_double_fn;
|
||||
typedef boost::function3<IfcGeom::IteratorImplementation<double, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&> iterator_double_double_fn;
|
||||
typedef boost::function4<IfcGeom::IteratorImplementation<float, float>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, int> iterator_float_float_fn;
|
||||
typedef boost::function4<IfcGeom::IteratorImplementation<float, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, int> iterator_float_double_fn;
|
||||
typedef boost::function4<IfcGeom::IteratorImplementation<double, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, int> iterator_double_double_fn;
|
||||
|
||||
template <typename P, typename PP>
|
||||
struct get_factory_type {};
|
||||
@@ -50,7 +50,7 @@ class IteratorFactoryImplementation : public std::map<std::string, typename get_
|
||||
public:
|
||||
IteratorFactoryImplementation();
|
||||
void bind(const std::string& schema_name, typename get_factory_type<P, PP>::type fn);
|
||||
IfcGeom::IteratorImplementation<P, PP>* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&);
|
||||
IfcGeom::IteratorImplementation<P, PP>* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, int);
|
||||
};
|
||||
|
||||
template <typename P, typename PP>
|
||||
|
||||
@@ -7,6 +7,7 @@ import sys
|
||||
import time
|
||||
import operator
|
||||
import functools
|
||||
import multiprocessing
|
||||
|
||||
import OCC.AIS
|
||||
|
||||
@@ -54,6 +55,43 @@ from .. import version as ifcopenshell_version
|
||||
if ifcopenshell_version < "0.6":
|
||||
# not yet ported
|
||||
from .. import get_supertype
|
||||
|
||||
class geometry_creation_signals(QtCore.QObject):
|
||||
completed = QtCore.pyqtSignal('PyQt_PyObject')
|
||||
progress = QtCore.pyqtSignal('PyQt_PyObject')
|
||||
|
||||
class geometry_creation_thread(QtCore.QThread):
|
||||
def __init__(self, signals, settings, f):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.signals = signals
|
||||
self.settings = settings
|
||||
self.f = f
|
||||
|
||||
def run(self):
|
||||
t0 = time.time()
|
||||
|
||||
# detect concurrency from hardware, we need to have
|
||||
# at least two threads because otherwise the interface
|
||||
# is different
|
||||
# is different
|
||||
it = iterator(self.settings, self.f, max(2, multiprocessing.cpu_count()))
|
||||
if not it.initialize():
|
||||
self.signals.completed.emit([])
|
||||
return
|
||||
|
||||
def _():
|
||||
|
||||
old_progress = -1
|
||||
while True:
|
||||
shape = it.get()
|
||||
|
||||
if shape:
|
||||
yield shape
|
||||
|
||||
if not it.next():
|
||||
break
|
||||
|
||||
self.signals.completed.emit((it, self.f, list(_())))
|
||||
|
||||
class configuration(object):
|
||||
def __init__(self):
|
||||
@@ -393,62 +431,59 @@ class application(QtWidgets.QApplication):
|
||||
self.product_to_ais = {}
|
||||
self.counter = 0
|
||||
self.window = widget
|
||||
self.thread = None
|
||||
|
||||
def initialize(self):
|
||||
self.InitDriver()
|
||||
self._display.Select = self.HandleSelection
|
||||
|
||||
def load_file(self, f, setting=None):
|
||||
|
||||
if setting is None:
|
||||
setting = settings()
|
||||
setting.set(setting.USE_PYTHON_OPENCASCADE, True)
|
||||
|
||||
def finished(self, file_shapes):
|
||||
it, f, shapes = file_shapes
|
||||
v = self._display
|
||||
|
||||
|
||||
t = {0: time.time()}
|
||||
|
||||
def update(dt=None):
|
||||
t1 = time.time()
|
||||
if t1 - t[0] > (dt or -1):
|
||||
if dt is None or t1 - t[0] > dt:
|
||||
v.FitAll()
|
||||
v.Repaint()
|
||||
t[0] = t1
|
||||
|
||||
terminate = [False]
|
||||
self.window.window_closed.connect(lambda *args: operator.setitem(terminate, 0, True))
|
||||
|
||||
t0 = time.time()
|
||||
|
||||
it = iterator(setting, f)
|
||||
if not it.initialize():
|
||||
return
|
||||
|
||||
old_progress = -1
|
||||
while True:
|
||||
if terminate[0]:
|
||||
break
|
||||
shape = it.get()
|
||||
product = f[shape.data.id]
|
||||
|
||||
for shape in shapes:
|
||||
ais = display_shape(shape, viewer_handle=v)
|
||||
product = f[shape.data.id]
|
||||
|
||||
ais.GetObject().SetSelectionPriority(self.counter)
|
||||
self.ais_to_product[self.counter] = product
|
||||
self.product_to_ais[product] = ais
|
||||
self.counter += 1
|
||||
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
if product.is_a() in {'IfcSpace', 'IfcOpeningElement'}:
|
||||
v.Context.Erase(ais, True)
|
||||
progress = it.progress() // 2
|
||||
if progress > old_progress:
|
||||
print("\r[" + "#" * progress + " " * (50 - progress) + "]", end="")
|
||||
old_progress = progress
|
||||
if not it.next():
|
||||
break
|
||||
update(0.2)
|
||||
|
||||
print("\rOpened file in %.2f seconds%s" % (time.time() - t0, " " * 25))
|
||||
|
||||
|
||||
update(1.)
|
||||
|
||||
update()
|
||||
|
||||
self.thread = None
|
||||
|
||||
def load_file(self, f, setting=None):
|
||||
|
||||
if self.thread is not None:
|
||||
return
|
||||
|
||||
if setting is None:
|
||||
setting = settings()
|
||||
setting.set(setting.USE_PYTHON_OPENCASCADE, True)
|
||||
|
||||
self.signals = geometry_creation_signals()
|
||||
thread = self.thread = geometry_creation_thread(self.signals, setting, f)
|
||||
self.window.window_closed.connect(lambda *args: thread.terminate())
|
||||
self.signals.completed.connect(self.finished)
|
||||
self.thread.start()
|
||||
|
||||
def select(self, product):
|
||||
ais = self.product_to_ais.get(product)
|
||||
|
||||
@@ -47,9 +47,11 @@ def wrap_shape_creation(settings, shape):
|
||||
if has_occ:
|
||||
from . import occ_utils as utils
|
||||
|
||||
def wrap_shape_creation(settings, shape): return utils.create_shape_from_serialization(shape) if getattr(settings,
|
||||
'use_python_opencascade',
|
||||
False) else shape
|
||||
def wrap_shape_creation(settings, shape):
|
||||
if getattr(settings, 'use_python_opencascade', False):
|
||||
return utils.create_shape_from_serialization(shape)
|
||||
else:
|
||||
return shape
|
||||
|
||||
|
||||
# Subclass the settings module to provide an additional
|
||||
@@ -77,13 +79,13 @@ _iterator = ifcopenshell_wrapper.iterator_double_precision
|
||||
|
||||
# Make sure people are able to use python's platform agnostic paths
|
||||
class iterator(_iterator):
|
||||
def __init__(self, settings, file_or_filename):
|
||||
def __init__(self, settings, file_or_filename, num_threads = 1):
|
||||
self.settings = settings
|
||||
if isinstance(file_or_filename, file):
|
||||
file_or_filename = file_or_filename.wrapped_data
|
||||
else:
|
||||
file_or_filename = os.path.abspath(file_or_filename)
|
||||
_iterator.__init__(self, settings, file_or_filename)
|
||||
_iterator.__init__(self, settings, file_or_filename, num_threads)
|
||||
|
||||
if has_occ:
|
||||
def get(self):
|
||||
|
||||
@@ -85,112 +85,162 @@ IfcCharacterDecoder::IfcCharacterDecoder(IfcParse::IfcSpfStream* f) {
|
||||
IfcCharacterDecoder::~IfcCharacterDecoder() {
|
||||
}
|
||||
|
||||
IfcCharacterDecoder::operator std::string() {
|
||||
unsigned int parse_state = 0;
|
||||
builder_.clear();
|
||||
builder_.push_back('\'');
|
||||
char current_char;
|
||||
int codepage = 1;
|
||||
unsigned int hex = 0;
|
||||
unsigned int hex_count = 0;
|
||||
namespace {
|
||||
static unsigned int reference_helper = 0;
|
||||
|
||||
while ( (current_char = file->Peek()) != 0 ) {
|
||||
if ( EXPECTS_CHARACTER(parse_state) ) {
|
||||
builder_.push_back(IfcUtil::convert_codepage(codepage, current_char + 0x80));
|
||||
parse_state = 0;
|
||||
} else if ( current_char == '\'' && ! parse_state ) {
|
||||
parse_state = APOSTROPHE;
|
||||
} else if ( current_char == '\\' && ! parse_state ) {
|
||||
parse_state = FIRST_SOLIDUS;
|
||||
} else if ( current_char == '\\' && EXPECTS_SOLIDUS(parse_state) ) {
|
||||
if ( parse_state & ALPHABET_DEFINITION ||
|
||||
parse_state & IGNORED_DIRECTIVE ||
|
||||
parse_state & ENDEXTENDED_0 ) parse_state = hex = hex_count = 0;
|
||||
else if ( parse_state & ENCOUNTERED_HEX ) {
|
||||
parse_state += THIRD_SOLIDUS;
|
||||
parse_state -= ENCOUNTERED_HEX;
|
||||
class pure_impure_helper {
|
||||
private:
|
||||
bool pure_;
|
||||
IfcParse::IfcSpfStream* stream_;
|
||||
unsigned int& pointer_;
|
||||
std::wstring builder_;
|
||||
|
||||
char peek() {
|
||||
if (pure_) {
|
||||
return stream_->peek_at(pointer_);
|
||||
} else {
|
||||
return stream_->Peek();
|
||||
}
|
||||
else parse_state += SECOND_SOLIDUS;
|
||||
} else if ( current_char == 'X' && EXPECTS_ENDEXTENDED_X(parse_state) ) {
|
||||
parse_state += ENDEXTENDED_X;
|
||||
} else if ( current_char == '0' && EXPECTS_ENDEXTENDED_0(parse_state) ) {
|
||||
parse_state += ENDEXTENDED_0;
|
||||
} else if ( current_char == 'X' && EXPECTS_ARBITRARY(parse_state) ) {
|
||||
parse_state += ARBITRARY;
|
||||
} else if ( current_char == '2' && EXPECTS_ARBITRARY2(parse_state) ) {
|
||||
parse_state += EXTENDED2;
|
||||
} else if ( current_char == '4' && EXPECTS_ARBITRARY2(parse_state) ) {
|
||||
parse_state += EXTENDED2 + EXTENDED4;
|
||||
} else if ( current_char == 'P' && EXPECTS_ALPHABET(parse_state) ) {
|
||||
parse_state += ALPHABET;
|
||||
} else if ( (current_char == 'N' || current_char == 'F') && EXPECTS_N_OR_F(parse_state) ) {
|
||||
parse_state += IGNORED_DIRECTIVE;
|
||||
} else if ( IS_VALID_ALPHABET_DEFINITION(current_char) && EXPECTS_ALPHABET_DEFINITION(parse_state) ) {
|
||||
codepage = current_char - 0x40;
|
||||
parse_state += ALPHABET_DEFINITION;
|
||||
} else if ( current_char == 'S' && EXPECTS_PAGE(parse_state) ) {
|
||||
parse_state += PAGE;
|
||||
} else if ( IS_HEXADECIMAL(current_char) && EXPECTS_HEX(parse_state) ) {
|
||||
hex <<= 4;
|
||||
parse_state += HEX((++hex_count));
|
||||
hex += HEX_TO_INT(current_char);
|
||||
if ( (hex_count == 2 && !(parse_state & EXTENDED2)) ||
|
||||
(hex_count == 4 && !(parse_state & EXTENDED4)) ||
|
||||
(hex_count == 8) )
|
||||
{
|
||||
builder_.push_back(hex);
|
||||
if ( hex_count == 2 ) parse_state = 0;
|
||||
else {
|
||||
CLEAR_HEX(parse_state);
|
||||
parse_state |= ENCOUNTERED_HEX;
|
||||
}
|
||||
hex = hex_count = 0;
|
||||
}
|
||||
} else if ( parse_state && !(
|
||||
(current_char == '\\' && parse_state == FIRST_SOLIDUS) ||
|
||||
(current_char == '\'' && parse_state == APOSTROPHE)
|
||||
) ) {
|
||||
if ( parse_state == APOSTROPHE && current_char != '\'' ) break;
|
||||
throw IfcInvalidTokenException(file->Tell(), current_char);
|
||||
} else {
|
||||
parse_state = hex = hex_count = 0;
|
||||
builder_.push_back(current_char);
|
||||
}
|
||||
file->Inc();
|
||||
}
|
||||
builder_.push_back('\'');
|
||||
|
||||
if (mode == UTF8) {
|
||||
return IfcUtil::convert_utf8(builder_);
|
||||
} else if (mode == SUBSTITUTE) {
|
||||
std::string r;
|
||||
r.reserve(builder_.size());
|
||||
const char& sub = substitution_character;
|
||||
std::transform(builder_.begin(), builder_.end(), std::back_inserter(r), [&sub](wchar_t c) {
|
||||
if (c >= 0x20 && c <= 0x7e) {
|
||||
return (char)c;
|
||||
unsigned int tell() {
|
||||
if (pure_) {
|
||||
return pointer_;
|
||||
} else {
|
||||
return sub;
|
||||
return stream_->Tell();
|
||||
}
|
||||
});
|
||||
return r;
|
||||
} else if (mode == ESCAPE) {
|
||||
std::stringstream str;
|
||||
str << std::hex << std::setw(4) << std::setfill('0');
|
||||
std::for_each(builder_.begin(), builder_.end(), [&str](wchar_t c) {
|
||||
if (c >= 0x20 && c <= 0x7e) {
|
||||
str.put((char)c);
|
||||
}
|
||||
|
||||
void increment() {
|
||||
if (pure_) {
|
||||
stream_->increment_at(pointer_);
|
||||
} else {
|
||||
str << "\\u" << c;
|
||||
stream_->Inc();
|
||||
}
|
||||
});
|
||||
return str.str();
|
||||
} else {
|
||||
throw IfcParse::IfcException("Invalid conversion mode");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
pure_impure_helper(IfcParse::IfcSpfStream* stream)
|
||||
: pure_(false), stream_(stream), pointer_(reference_helper)
|
||||
{}
|
||||
|
||||
pure_impure_helper(IfcParse::IfcSpfStream* stream, unsigned int& pointer)
|
||||
: pure_(true), stream_(stream), pointer_(pointer)
|
||||
{}
|
||||
|
||||
std::string get(IfcParse::IfcCharacterDecoder::ConversionMode mode, char substitution_character) {
|
||||
unsigned int parse_state = 0;
|
||||
builder_.clear();
|
||||
builder_.push_back('\'');
|
||||
char current_char;
|
||||
int codepage = 1;
|
||||
unsigned int hex = 0;
|
||||
unsigned int hex_count = 0;
|
||||
|
||||
while ((current_char = peek()) != 0) {
|
||||
if (EXPECTS_CHARACTER(parse_state)) {
|
||||
builder_.push_back(IfcUtil::convert_codepage(codepage, current_char + 0x80));
|
||||
parse_state = 0;
|
||||
} else if (current_char == '\'' && !parse_state) {
|
||||
parse_state = APOSTROPHE;
|
||||
} else if (current_char == '\\' && !parse_state) {
|
||||
parse_state = FIRST_SOLIDUS;
|
||||
} else if (current_char == '\\' && EXPECTS_SOLIDUS(parse_state)) {
|
||||
if (parse_state & ALPHABET_DEFINITION ||
|
||||
parse_state & IGNORED_DIRECTIVE ||
|
||||
parse_state & ENDEXTENDED_0) parse_state = hex = hex_count = 0;
|
||||
else if (parse_state & ENCOUNTERED_HEX) {
|
||||
parse_state += THIRD_SOLIDUS;
|
||||
parse_state -= ENCOUNTERED_HEX;
|
||||
} else parse_state += SECOND_SOLIDUS;
|
||||
} else if (current_char == 'X' && EXPECTS_ENDEXTENDED_X(parse_state)) {
|
||||
parse_state += ENDEXTENDED_X;
|
||||
} else if (current_char == '0' && EXPECTS_ENDEXTENDED_0(parse_state)) {
|
||||
parse_state += ENDEXTENDED_0;
|
||||
} else if (current_char == 'X' && EXPECTS_ARBITRARY(parse_state)) {
|
||||
parse_state += ARBITRARY;
|
||||
} else if (current_char == '2' && EXPECTS_ARBITRARY2(parse_state)) {
|
||||
parse_state += EXTENDED2;
|
||||
} else if (current_char == '4' && EXPECTS_ARBITRARY2(parse_state)) {
|
||||
parse_state += EXTENDED2 + EXTENDED4;
|
||||
} else if (current_char == 'P' && EXPECTS_ALPHABET(parse_state)) {
|
||||
parse_state += ALPHABET;
|
||||
} else if ((current_char == 'N' || current_char == 'F') && EXPECTS_N_OR_F(parse_state)) {
|
||||
parse_state += IGNORED_DIRECTIVE;
|
||||
} else if (IS_VALID_ALPHABET_DEFINITION(current_char) && EXPECTS_ALPHABET_DEFINITION(parse_state)) {
|
||||
codepage = current_char - 0x40;
|
||||
parse_state += ALPHABET_DEFINITION;
|
||||
} else if (current_char == 'S' && EXPECTS_PAGE(parse_state)) {
|
||||
parse_state += PAGE;
|
||||
} else if (IS_HEXADECIMAL(current_char) && EXPECTS_HEX(parse_state)) {
|
||||
hex <<= 4;
|
||||
parse_state += HEX((++hex_count));
|
||||
hex += HEX_TO_INT(current_char);
|
||||
if ((hex_count == 2 && !(parse_state & EXTENDED2)) ||
|
||||
(hex_count == 4 && !(parse_state & EXTENDED4)) ||
|
||||
(hex_count == 8)) {
|
||||
builder_.push_back(hex);
|
||||
if (hex_count == 2) parse_state = 0;
|
||||
else {
|
||||
CLEAR_HEX(parse_state);
|
||||
parse_state |= ENCOUNTERED_HEX;
|
||||
}
|
||||
hex = hex_count = 0;
|
||||
}
|
||||
} else if (parse_state && !(
|
||||
(current_char == '\\' && parse_state == FIRST_SOLIDUS) ||
|
||||
(current_char == '\'' && parse_state == APOSTROPHE)
|
||||
)) {
|
||||
if (parse_state == APOSTROPHE && current_char != '\'') break;
|
||||
throw IfcInvalidTokenException(tell(), current_char);
|
||||
} else {
|
||||
parse_state = hex = hex_count = 0;
|
||||
builder_.push_back(current_char);
|
||||
}
|
||||
increment();
|
||||
}
|
||||
builder_.push_back('\'');
|
||||
|
||||
if (mode == IfcParse::IfcCharacterDecoder::UTF8) {
|
||||
return IfcUtil::convert_utf8(builder_);
|
||||
} else if (mode == IfcParse::IfcCharacterDecoder::SUBSTITUTE) {
|
||||
std::string r;
|
||||
r.reserve(builder_.size());
|
||||
std::transform(builder_.begin(), builder_.end(), std::back_inserter(r), [&substitution_character](wchar_t c) {
|
||||
if (c >= 0x20 && c <= 0x7e) {
|
||||
return (char)c;
|
||||
} else {
|
||||
return substitution_character;
|
||||
}
|
||||
});
|
||||
return r;
|
||||
} else if (mode == IfcParse::IfcCharacterDecoder::ESCAPE) {
|
||||
std::stringstream str;
|
||||
str << std::hex << std::setw(4) << std::setfill('0');
|
||||
std::for_each(builder_.begin(), builder_.end(), [&str](wchar_t c) {
|
||||
if (c >= 0x20 && c <= 0x7e) {
|
||||
str.put((char)c);
|
||||
} else {
|
||||
str << "\\u" << c;
|
||||
}
|
||||
});
|
||||
return str.str();
|
||||
} else {
|
||||
throw IfcParse::IfcException("Invalid conversion mode");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void IfcCharacterDecoder::dryRun() {
|
||||
IfcCharacterDecoder::operator std::string() {
|
||||
return pure_impure_helper(file).get(mode, substitution_character);
|
||||
}
|
||||
|
||||
std::string IfcCharacterDecoder::get(unsigned int& ptr) {
|
||||
return pure_impure_helper(file, ptr).get(mode, substitution_character);
|
||||
}
|
||||
|
||||
void IfcCharacterDecoder::skip() {
|
||||
unsigned int parse_state = 0;
|
||||
char current_char;
|
||||
unsigned int hex_count = 0;
|
||||
|
||||
@@ -43,7 +43,6 @@ namespace IfcParse {
|
||||
class IFC_PARSE_API IfcCharacterDecoder {
|
||||
private:
|
||||
IfcParse::IfcSpfStream* file;
|
||||
std::wstring builder_;
|
||||
int codepage_;
|
||||
public:
|
||||
enum ConversionMode {SUBSTITUTE, UTF8, ESCAPE};
|
||||
@@ -51,8 +50,15 @@ namespace IfcParse {
|
||||
static char substitution_character;
|
||||
IfcCharacterDecoder(IfcParse::IfcSpfStream* file);
|
||||
~IfcCharacterDecoder();
|
||||
void dryRun();
|
||||
// Only advances the underlying token stream read pointer
|
||||
// to the next token.
|
||||
void skip();
|
||||
// Gets a decoded string representation at the token stream
|
||||
// read pointer and advances the underlying token stream.
|
||||
operator std::string();
|
||||
// Gets a decoded string representation at the offset provided,
|
||||
// does not mutate the underlying token stream read pointer.
|
||||
std::string get(unsigned int&);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -199,8 +199,9 @@ public:
|
||||
|
||||
std::string createTimestamp() const;
|
||||
|
||||
void load(const IfcEntityInstanceData&);
|
||||
size_t load(unsigned entity_instance_name, Argument**& attributes, size_t num_attributes);
|
||||
void seek_to(const IfcEntityInstanceData& data);
|
||||
void try_read_semicolon();
|
||||
|
||||
void register_inverse(unsigned, Token);
|
||||
void register_inverse(unsigned, IfcUtil::IfcBaseClass*);
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/version.hpp>
|
||||
|
||||
#include <mutex>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
@@ -114,6 +115,9 @@ void Logger::SetOutput(std::wostream* l1, std::wostream* l2) {
|
||||
}
|
||||
|
||||
void Logger::Message(Logger::Severity type, const std::string& message, const IfcUtil::IfcBaseClass* instance) {
|
||||
static std::mutex m;
|
||||
std::lock_guard<std::mutex> lk(m);
|
||||
|
||||
if (type > max_severity) {
|
||||
max_severity = type;
|
||||
}
|
||||
|
||||
+55
-27
@@ -17,17 +17,6 @@
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctime>
|
||||
#include <boost/circular_buffer.hpp>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/math/special_functions/fpclassify.hpp>
|
||||
|
||||
#include "../ifcparse/IfcCharacterDecoder.h"
|
||||
#include "../ifcparse/IfcParse.h"
|
||||
#include "../ifcparse/IfcException.h"
|
||||
@@ -42,6 +31,18 @@
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#endif
|
||||
|
||||
#include <set>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/circular_buffer.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/math/special_functions/fpclassify.hpp>
|
||||
|
||||
#define PERMISSIVE_FLOAT
|
||||
|
||||
using namespace IfcParse;
|
||||
@@ -251,9 +252,11 @@ void IfcSpfStream::Inc() {
|
||||
eof = true;
|
||||
return;
|
||||
}
|
||||
/// @todo: Shouldn't this be a loop of some kind
|
||||
const char current = IfcSpfStream::Peek();
|
||||
if ( current == '\n' || current == '\r' ) IfcSpfStream::Inc();
|
||||
if (current == '\n' || current == '\r') {
|
||||
// NB this is recursive. It might as well be a loop.
|
||||
IfcSpfStream::Inc();
|
||||
}
|
||||
}
|
||||
|
||||
IfcSpfLexer::IfcSpfLexer(IfcParse::IfcSpfStream *s, IfcParse::IfcFile* f) {
|
||||
@@ -331,34 +334,46 @@ Token IfcSpfLexer::Next() {
|
||||
len ++;
|
||||
|
||||
// If a string is encountered defer processing to the IfcCharacterDecoder
|
||||
if ( c == '\'' ) decoder->dryRun();
|
||||
if ( c == '\'' ) decoder->skip();
|
||||
}
|
||||
if ( len ) return GeneralTokenPtr(this, pos, stream->Tell());
|
||||
else return NoneTokenPtr();
|
||||
}
|
||||
|
||||
bool IfcSpfStream::is_eof_at(unsigned int local_ptr) {
|
||||
return local_ptr >= len;
|
||||
}
|
||||
|
||||
void IfcSpfStream::increment_at(unsigned int& local_ptr) {
|
||||
if (++local_ptr == len) {
|
||||
return;
|
||||
}
|
||||
const char current = IfcSpfStream::peek_at(local_ptr);
|
||||
if (current == '\n' || current == '\r') IfcSpfStream::increment_at(local_ptr);
|
||||
}
|
||||
|
||||
char IfcSpfStream::peek_at(unsigned int local_ptr) {
|
||||
return buffer[local_ptr];
|
||||
}
|
||||
|
||||
//
|
||||
// Reads a std::string from the file at specified offset
|
||||
// Omits whitespace and comments
|
||||
//
|
||||
void IfcSpfLexer::TokenString(unsigned int offset, std::string &buffer) {
|
||||
const bool was_eof = stream->eof;
|
||||
unsigned int old_offset = stream->Tell();
|
||||
stream->Seek(offset);
|
||||
buffer.clear();
|
||||
while ( ! stream->eof ) {
|
||||
char c = stream->Peek();
|
||||
while (!stream->is_eof_at(offset)) {
|
||||
char c = stream->peek_at(offset);
|
||||
if ( buffer.size() && (c == '(' || c == ')' || c == '=' || c == ',' || c == ';' || c == '/') ) break;
|
||||
stream->Inc();
|
||||
stream->increment_at(offset);
|
||||
if ( c == ' ' || c == '\r' || c == '\n' || c == '\t' ) continue;
|
||||
else if ( c == '\'' ) {
|
||||
buffer = *decoder;
|
||||
// todo, make decoder use local offset ptr
|
||||
buffer = decoder->get(offset);
|
||||
break;
|
||||
}
|
||||
else buffer.push_back(c);
|
||||
}
|
||||
if ( was_eof ) stream->eof = true;
|
||||
else stream->Seek(old_offset);
|
||||
}
|
||||
|
||||
//Note: according to STEP standard, there may be newlines in tokens
|
||||
@@ -887,14 +902,16 @@ IfcEntityInstanceData* IfcParse::read(unsigned int i, IfcFile* f, boost::optiona
|
||||
return e;
|
||||
}
|
||||
|
||||
void IfcParse::IfcFile::load(const IfcEntityInstanceData& data) {
|
||||
void IfcParse::IfcFile::seek_to(const IfcEntityInstanceData& data) {
|
||||
if (tokens->stream->Tell() != data.offset_in_file()) {
|
||||
tokens->stream->Seek(data.offset_in_file());
|
||||
Token datatype = tokens->Next();
|
||||
if (!TokenFunc::isKeyword(datatype)) throw IfcException("Unexpected token while parsing entity instance");
|
||||
}
|
||||
tokens->Next();
|
||||
load(data.id(), data.attributes(), data.getArgumentCount());
|
||||
}
|
||||
|
||||
void IfcParse::IfcFile::try_read_semicolon() {
|
||||
unsigned int old_offset = tokens->stream->Tell();
|
||||
Token semilocon = tokens->Next();
|
||||
if (!TokenFunc::isOperator(semilocon, ';')) {
|
||||
@@ -984,15 +1001,26 @@ unsigned IfcEntityInstanceData::set_id(boost::optional<unsigned> i) {
|
||||
// Returns the entities of Entity type that have this entity in their ArgumentList
|
||||
//
|
||||
IfcEntityList::ptr IfcEntityInstanceData::getInverse(const IfcParse::declaration* type, int attribute_index) const {
|
||||
static std::mutex m;
|
||||
std::lock_guard<std::mutex> lk(m);
|
||||
|
||||
return file->getInverse(id_, type, attribute_index);
|
||||
}
|
||||
|
||||
void IfcEntityInstanceData::load() const {
|
||||
static std::recursive_mutex m;
|
||||
std::lock_guard<std::recursive_mutex> lk(m);
|
||||
|
||||
// type_ is 0 for header entities which have their size predetermined in code
|
||||
Argument** tmp_data = nullptr;
|
||||
if (type_ != 0) {
|
||||
attributes_ = new Argument*[getArgumentCount()];
|
||||
tmp_data = new Argument*[getArgumentCount()];
|
||||
}
|
||||
file->load(*this);
|
||||
file->seek_to(*this);
|
||||
file->load(id(), tmp_data, getArgumentCount());
|
||||
file->try_read_semicolon();
|
||||
// @todo does this need to be atomic somehow?
|
||||
attributes_ = tmp_data;
|
||||
}
|
||||
|
||||
IfcEntityInstanceData::IfcEntityInstanceData(const IfcEntityInstanceData& e) {
|
||||
|
||||
+15
-3
@@ -49,6 +49,17 @@
|
||||
|
||||
#include "../ifcparse/IfcSpfStream.h"
|
||||
|
||||
/* gcc doesn't know _Thread_local from C11 yet */
|
||||
#ifdef __GNUC__
|
||||
# define my_thread_local __thread
|
||||
#elif __STDC_VERSION__ >= 201112L
|
||||
# define my_thread_local _Thread_local
|
||||
#elif defined(_MSC_VER)
|
||||
# define my_thread_local __declspec(thread)
|
||||
#else
|
||||
# error Cannot define thread_local
|
||||
#endif
|
||||
|
||||
namespace IfcParse {
|
||||
|
||||
class IfcFile;
|
||||
@@ -141,12 +152,13 @@ namespace IfcParse {
|
||||
class IFC_PARSE_API IfcSpfLexer {
|
||||
private:
|
||||
IfcCharacterDecoder* decoder;
|
||||
//storage for temporary string without allocation
|
||||
mutable std::string _tempString;
|
||||
unsigned int skipWhitespace();
|
||||
unsigned int skipComment();
|
||||
public:
|
||||
std::string &GetTempString() const { return _tempString; }
|
||||
std::string &GetTempString() const {
|
||||
static my_thread_local std::string s;
|
||||
return s;
|
||||
}
|
||||
IfcSpfStream* stream;
|
||||
IfcFile* file;
|
||||
IfcSpfLexer(IfcSpfStream* s, IfcFile* f);
|
||||
|
||||
@@ -72,6 +72,10 @@ namespace IfcParse {
|
||||
void Seek(unsigned int offset);
|
||||
/// Returns the cursor position
|
||||
unsigned int Tell();
|
||||
|
||||
bool is_eof_at(unsigned int);
|
||||
void increment_at(unsigned int&);
|
||||
char peek_at(unsigned int);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user