diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 9141e31f41..01324129c8 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -220,7 +220,7 @@ int main(int argc, char** argv) { ("calculate-quantities", "Calculate or fix the physical quantity definitions " "based on an interpretation of the geometry when exporting IFC"); - size_t num_threads; + int num_threads; po::options_description geom_options("Geometry options"); geom_options.add_options() @@ -735,7 +735,7 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - if (num_threads == 0) { + if (num_threads <= 0) { num_threads = std::thread::hardware_concurrency(); Logger::Notice("Using " + std::to_string(num_threads) + " threads"); } diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.cpp b/src/ifcgeom/IfcGeomIteratorImplementation.cpp index 3d3616bf21..1206f0fd5f 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.cpp +++ b/src/ifcgeom/IfcGeomIteratorImplementation.cpp @@ -14,7 +14,7 @@ namespace IfcGeom { namespace { template struct MAKE_TYPE_NAME(factory_t) { - IfcGeom::IteratorImplementation* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, size_t num_threads) const { + IfcGeom::IteratorImplementation* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, int num_threads) const { return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)(settings, file, filters, num_threads); } }; diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.h b/src/ifcgeom/IfcGeomIteratorImplementation.h index c1d342f3c5..aff570af0b 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.h +++ b/src/ifcgeom/IfcGeomIteratorImplementation.h @@ -89,6 +89,8 @@ #include "../ifcgeom_schema_agnostic/IfcGeomFilter.h" #include "../ifcgeom_schema_agnostic/IteratorImplementation.h" +#include + // The infamous min & max Win32 #defines can leak here from OCE depending on the build configuration #ifdef min #undef min @@ -176,7 +178,8 @@ namespace IfcGeom { class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation { private: - size_t num_threads_; + int num_threads_; + std::atomic progress_; std::vector> tasks_; std::vector*> all_processed_elements_; std::vector*> all_processed_native_elements_; @@ -437,10 +440,10 @@ namespace IfcGeom { fu.get(); processed += 1; - const int progress = processed * 50 / tasks_.size(); - if (progress != old_progress) { - Logger::ProgressBar(progress); - old_progress = progress; + progress_ = processed * 50 / tasks_.size(); + if (progress_ != old_progress) { + Logger::ProgressBar(progress_); + old_progress = progress_; } std::swap(threadpool[i], threadpool.back()); @@ -460,10 +463,10 @@ namespace IfcGeom { fu.get(); processed += 1; - const int progress = processed * 50 / tasks_.size(); - if (progress != old_progress) { - Logger::ProgressBar(progress); - old_progress = progress; + progress_ = processed * 50 / tasks_.size(); + if (progress_ != old_progress) { + Logger::ProgressBar(progress_); + old_progress = progress_; } } @@ -519,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; } @@ -946,7 +955,7 @@ namespace IfcGeom { bool owns_ifc_file; public: - MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, size_t num_threads) + MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, int num_threads) : settings(settings) , ifc_file(file) , filters_(filters) diff --git a/src/ifcgeom_schema_agnostic/IfcGeomIterator.h b/src/ifcgeom_schema_agnostic/IfcGeomIterator.h index 9d46b39ac3..50d15c1917 100644 --- a/src/ifcgeom_schema_agnostic/IfcGeomIterator.h +++ b/src/ifcgeom_schema_agnostic/IfcGeomIterator.h @@ -83,7 +83,7 @@ namespace IfcGeom { IteratorImplementation* implementation_; public: - Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, size_t num_threads = 1) + Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, int num_threads = 1) : file_(file) , settings_(settings) { diff --git a/src/ifcgeom_schema_agnostic/IteratorImplementation.cpp b/src/ifcgeom_schema_agnostic/IteratorImplementation.cpp index cf2f89f2df..7cc968f6bc 100644 --- a/src/ifcgeom_schema_agnostic/IteratorImplementation.cpp +++ b/src/ifcgeom_schema_agnostic/IteratorImplementation.cpp @@ -31,7 +31,7 @@ void IteratorFactoryImplementation::bind(const std::string& schema_name, } template -IfcGeom::IteratorImplementation* IteratorFactoryImplementation::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, size_t num_threads) { +IfcGeom::IteratorImplementation* IteratorFactoryImplementation::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, int num_threads) { const std::string schema_name_lower = boost::to_lower_copy(schema_name); typename std::map::type>::const_iterator it; it = this->find(schema_name_lower); diff --git a/src/ifcgeom_schema_agnostic/IteratorImplementation.h b/src/ifcgeom_schema_agnostic/IteratorImplementation.h index 5622c0eec8..0d6ec96c4c 100644 --- a/src/ifcgeom_schema_agnostic/IteratorImplementation.h +++ b/src/ifcgeom_schema_agnostic/IteratorImplementation.h @@ -23,9 +23,9 @@ namespace IfcGeom { class BRepElement; } -typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, size_t> iterator_float_float_fn; -typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, size_t> iterator_float_double_fn; -typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, size_t> iterator_double_double_fn; +typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, int> iterator_float_float_fn; +typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, int> iterator_float_double_fn; +typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, int> iterator_double_double_fn; template struct get_factory_type {}; @@ -50,7 +50,7 @@ class IteratorFactoryImplementation : public std::map::type fn); - IfcGeom::IteratorImplementation* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, size_t); + IfcGeom::IteratorImplementation* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, int); }; template diff --git a/src/ifcopenshell-python/ifcopenshell/geom/app.py b/src/ifcopenshell-python/ifcopenshell/geom/app.py index 9fcd3191a0..2005416ed9 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/app.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/app.py @@ -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) diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index a73800b915..55ff561164 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -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):