From e2f79ec1bacc4ab46e5bb5b67fc6b09ca1eb2776 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Sun, 31 Dec 2017 12:20:13 +0100 Subject: [PATCH] Work on python wrapper --- .travis.yml | 13 +---- cmake/CMakeLists.txt | 9 --- .../ifcopenshell/__init__.py | 4 +- .../ifcopenshell/entity_instance.py | 4 +- src/ifcopenshell-python/ifcopenshell/file.py | 11 +++- src/ifcopenshell-python/ifcopenshell/main.py | 2 - src/ifcparse/IfcParse.cpp | 55 ++++++++++-------- src/ifcparse/IfcSchema.cpp | 2 +- src/ifcparse/IfcSchema.h | 4 +- src/ifcwrap/IfcParseWrapper.i | 57 ++++++++++++++----- src/ifcwrap/IfcPython.i | 1 + test/tests.py | 4 +- 12 files changed, 94 insertions(+), 72 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9e0297c4bd..fc81f444d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,19 +51,8 @@ script: - cd cmake - mkdir build-ifc2x3 build-ifc4 - cd build-ifc2x3 - - cmake -DCOLLADA_SUPPORT=True -DOPENCOLLADA_INCLUDE_DIR=/usr/local/include/opencollada -DOPENCOLLADA_LIBRARY_DIR=/usr/local/lib/opencollada -DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu -DUSE_IFC4=False -DBUILD_IFCPYTHON=True -DUNICODE_SUPPORT=True -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu -DPYTHON_LIBRARY=/usr/lib/python2.7/config-x86_64-linux-gnu/libpython2.7.so -DPYTHON_INCLUDE_DIR=/usr/include/python2.7 -DPYTHON_EXECUTABLE=/usr/bin/python2.7 .. + - cmake -DCOLLADA_SUPPORT=True -DOPENCOLLADA_INCLUDE_DIR=/usr/local/include/opencollada -DOPENCOLLADA_LIBRARY_DIR=/usr/local/lib/opencollada -DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu -DBUILD_EXAMPLES=Off -DBUILD_IFCPYTHON=True -DUNICODE_SUPPORT=True -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu -DPYTHON_LIBRARY=/usr/lib/python2.7/config-x86_64-linux-gnu/libpython2.7.so -DPYTHON_INCLUDE_DIR=/usr/include/python2.7 -DPYTHON_EXECUTABLE=/usr/bin/python2.7 .. - make -j - sudo make install - - ./examples/IfcOpenHouse - - ./examples/IfcAdvancedHouse - - test -f IfcOpenHouse.ifc - - test -f IfcAdvancedHouse.ifc - cd ../../test - /usr/bin/python2.7 tests.py - - cd ../cmake/build-ifc4 - - cmake -DCOLLADA_SUPPORT=True -DOPENCOLLADA_INCLUDE_DIR=/usr/local/include/opencollada -DOPENCOLLADA_LIBRARY_DIR=/usr/local/lib/opencollada -DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu -DUSE_IFC4=True -DBUILD_IFCPYTHON=True -DUNICODE_SUPPORT=True -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu -DPYTHON_LIBRARY=/usr/lib/python2.7/config-x86_64-linux-gnu/libpython2.7.so -DPYTHON_INCLUDE_DIR=/usr/include/python2.7 -DPYTHON_EXECUTABLE=/usr/bin/python2.7 .. - - make -j - - ./examples/IfcOpenHouse - - ./examples/IfcAdvancedHouse - - test -f IfcOpenHouse.ifc - - test -f IfcAdvancedHouse.ifc diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 2857e97f78..6fb309169f 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -25,7 +25,6 @@ OPTION(UNICODE_SUPPORT "Build IfcOpenShell with Unicode support (requires ICU)." OPTION(COLLADA_SUPPORT "Build IfcConvert with COLLADA support (requires OpenCOLLADA)." ON) OPTION(ENABLE_BUILD_OPTIMIZATIONS "Enable certain compiler and linker optimizations on RelWithDebInfo and Release builds." OFF) OPTION(IFCCONVERT_DOUBLE_PRECISION "IfcConvert: Use double precision floating-point numbers." ON) -OPTION(USE_IFC4 "Use IFC 4 instead of IFC 2x3 (full rebuild recommended when switching this)" OFF) OPTION(BUILD_IFCPYTHON "Build IfcPython." ON) OPTION(BUILD_EXAMPLES "Build example applications." ON) OPTION(USE_VLD "Use Visual Leak Detector for debugging memory leaks, MSVC-only." OFF) @@ -488,14 +487,6 @@ if(COMPILE_SCHEMA) list(REMOVE_ITEM IFC_RELEASE_NOT_USED "4") add_definitions(-DUSE_IFC4) endif() -else() - if(USE_IFC4) - add_definitions(-DUSE_IFC4) - set(IFC_RELEASE_NOT_USED "2x3") - else() - add_definitions(-DUSE_IFC2x3) # TODO Make all caps? i.e. USE_IFC2X3 - set(IFC_RELEASE_NOT_USED "4") - endif() endif() # Boost >= 1.58 requires BOOST_OPTIONAL_USE_OLD_DEFINITION_OF_NONE to build on some Linux distros. diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index f061122789..2ff399c8da 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -59,8 +59,8 @@ from .file import file from .entity_instance import entity_instance -def open(fn=None): - return file(ifcopenshell_wrapper.open(os.path.abspath(fn))) if fn else file() +def open(fn): + return file(ifcopenshell_wrapper.open(os.path.abspath(fn))) def create_entity(type, *args, **kwargs): diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py index f2c88fa398..bb636fcd74 100644 --- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -51,8 +51,8 @@ class entity_instance(object): >>> #423=IfcProductDefinitionShape($,$,(#409,#421)) """ def __init__(self, e): - if isinstance(e, str): - e = ifcopenshell_wrapper.new_IfcBaseClass(e) + if isinstance(e, tuple): + e = ifcopenshell_wrapper.new_IfcBaseClass(*e) super(entity_instance, self).__setattr__('wrapped_data', e) def __getattr__(self, name): diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index 183ef690e0..3390f6a1b1 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -55,11 +55,16 @@ class file(object): print(products[0] == ifc_file[122] == ifc_file['2XQ$n5SLP5MBLyL442paFx']) >>> True """ - def __init__(self, f=None): - self.wrapped_data = f or ifcopenshell_wrapper.file() + def __init__(self, f=None, schema=None): + if f is not None: + self.wrapped_data = f + else: + args = filter(None, [schema]) + args = map(ifcopenshell_wrapper.schema_by_name, args) + self.wrapped_data = ifcopenshell_wrapper.file(*args) def create_entity(self, type, *args, **kwargs): - e = entity_instance(type) + e = entity_instance((self.schema, type)) self.wrapped_data.add(e.wrapped_data) e.wrapped_data.this.disown() attrs = list(enumerate(args)) + \ diff --git a/src/ifcopenshell-python/ifcopenshell/main.py b/src/ifcopenshell-python/ifcopenshell/main.py index 0111e0a355..e6835dbab2 100644 --- a/src/ifcopenshell-python/ifcopenshell/main.py +++ b/src/ifcopenshell-python/ifcopenshell/main.py @@ -24,6 +24,4 @@ from __future__ import print_function from . import ifcopenshell_wrapper version = ifcopenshell_wrapper.version() -schema_identifier = ifcopenshell_wrapper.schema_identifier() -get_supertype = ifcopenshell_wrapper.get_supertype get_log = ifcopenshell_wrapper.get_log diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index c7da43f20c..593b7874a7 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -1249,6 +1249,7 @@ void IfcFile::initialize_(IfcParse::IfcSpfStream* s) { MaxId = 0; tokens = 0; stream = 0; + schema_ = 0; setDefaultHeaderValues(); @@ -1270,8 +1271,6 @@ void IfcFile::initialize_(IfcParse::IfcSpfStream* s) { // Purposely empty catch block } - schema_ = 0; - if (schemas.size() == 1) { try { schema_ = IfcParse::schema_by_name(schemas.front()); @@ -1493,13 +1492,21 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity) { // information needs to be accounted for for IfcLengthMeasures. double conversion_factor = std::numeric_limits::quiet_NaN(); - std::vector attribute_types = entity->declaration().as_entity()->all_attributes(); - for (unsigned i = 0; i < we->getArgumentCount(); ++i) { Argument* attr = we->getArgument(i); IfcUtil::ArgumentType attr_type = attr->type(); - IfcParse::declaration* decl = attribute_types[i]->type_of_attribute()->as_named_type()->declared_type(); + IfcParse::declaration* decl = 0; + if (entity->declaration().as_entity()) { + decl = 0; + const parameter_type* pt = entity->declaration().as_entity()->attribute_by_index(i)->type_of_attribute(); + while (pt->as_aggregation_type()) { + pt = pt->as_aggregation_type()->type_of_element(); + } + if (pt->as_named_type()) { + decl = pt->as_named_type()->declared_type(); + } + } if (attr_type == IfcUtil::Argument_ENTITY_INSTANCE) { entity_entity_map_t::const_iterator eit = entity_file_map.find(*attr); @@ -1595,7 +1602,7 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity) { // The mapping by entity type is updated. const IfcParse::declaration* ty = &new_entity->declaration(); - { + if (ty->as_entity()) { IfcEntityList::ptr insts = instances_by_type_excl_subtypes(ty); if (!insts) { insts = IfcEntityList::ptr(new IfcEntityList()); @@ -1604,7 +1611,7 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity) { insts->push(new_entity); } - for (;;) { + for (; ty->as_entity();) { IfcEntityList::ptr insts = instances_by_type(ty); if (!insts) { insts = IfcEntityList::ptr(new IfcEntityList()); @@ -1620,7 +1627,7 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity) { } } - if (new_entity->declaration().as_entity()) { + if (ty->as_entity()) { int new_id = -1; if (!new_entity->data().file) { // For newly created entities ensure a valid ENTITY_INSTANCE_NAME is set @@ -1641,25 +1648,27 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity) { byid[new_id] = new_entity; } - // The mapping by reference is updated. - IfcEntityList::ptr entity_attributes(new IfcEntityList); - try { - entity_attributes = traverse(new_entity, 1); - } catch (const std::exception& e) { - Logger::Error(e); - } - - for (IfcEntityList::it it = entity_attributes->begin(); it != entity_attributes->end(); ++it) { - IfcUtil::IfcBaseClass* entity_attribute = *it; - if (*it == new_entity) continue; + if (ty->as_entity()) { + // The mapping by reference is updated. + IfcEntityList::ptr entity_attributes(new IfcEntityList); try { - if (entity_attribute->declaration().as_entity()) { - unsigned entity_attribute_id = entity_attribute->data().id(); - byref[entity_attribute_id].push_back(new_entity->data().id()); - } + entity_attributes = traverse(new_entity, 1); } catch (const std::exception& e) { Logger::Error(e); } + + for (IfcEntityList::it it = entity_attributes->begin(); it != entity_attributes->end(); ++it) { + IfcUtil::IfcBaseClass* entity_attribute = *it; + if (*it == new_entity) continue; + try { + if (entity_attribute->declaration().as_entity()) { + unsigned entity_attribute_id = entity_attribute->data().id(); + byref[entity_attribute_id].push_back(new_entity->data().id()); + } + } catch (const std::exception& e) { + Logger::Error(e); + } + } } return new_entity; diff --git a/src/ifcparse/IfcSchema.cpp b/src/ifcparse/IfcSchema.cpp index 733df0406e..0644110ee4 100644 --- a/src/ifcparse/IfcSchema.cpp +++ b/src/ifcparse/IfcSchema.cpp @@ -3,7 +3,7 @@ #include bool IfcParse::declaration::is(const std::string& name) const { - if (name_ == name) return true; + if (name_lower_ == boost::to_lower_copy(name)) return true; if (this->as_entity()) { return this->as_entity()->is(name); diff --git a/src/ifcparse/IfcSchema.h b/src/ifcparse/IfcSchema.h index 8a42eb20f3..7190609192 100644 --- a/src/ifcparse/IfcSchema.h +++ b/src/ifcparse/IfcSchema.h @@ -268,11 +268,11 @@ namespace IfcParse { } if (attr == 0) { if (index < attributes_.size()) { - return attributes_[index]; + attr = attributes_[index]; } index -= attributes_.size(); } - return 0; + return attr; } public: diff --git a/src/ifcwrap/IfcParseWrapper.i b/src/ifcwrap/IfcParseWrapper.i index 0836722e96..0f2199636f 100644 --- a/src/ifcwrap/IfcParseWrapper.i +++ b/src/ifcwrap/IfcParseWrapper.i @@ -24,10 +24,9 @@ private: IfcEntityInstanceData(); }; -%ignore IfcParse::IfcFile::Init; -%ignore IfcParse::IfcFile::entityByGuid; %ignore IfcParse::IfcFile::register_inverse; %ignore IfcParse::IfcFile::unregister_inverse; +%ignore IfcParse::IfcFile::schema; %ignore operator<<; %ignore IfcParse::FileDescription::FileDescription; @@ -44,15 +43,8 @@ private: %ignore IfcUtil::IfcBaseClass::is; -%rename("by_id") entityById; -%rename("by_type") entitiesByType; -%rename("__len__") getArgumentCount; -%rename("get_argument_type") getArgumentType; -%rename("get_argument_name") getArgumentName; -%rename("get_argument_index") getArgumentIndex; -%rename("get_argument_optionality") getArgumentOptionality; -%rename("get_attribute_names") getAttributeNames; -%rename("get_inverse_attribute_names") getInverseAttributeNames; +%rename("by_id") instance_by_id; +%rename("by_type") instances_by_type; %rename("entity_instance") IfcBaseClass; %rename("file") IfcFile; %rename("add") addEntity; @@ -102,10 +94,16 @@ static const std::string& helper_fn_declaration_get_name(const IfcParse::declara return ts; } + std::string schema_name() const { + if ($self->schema() == 0) return ""; + return $self->schema()->name(); + } + %pythoncode %{ if _newclass: # Hide the getters with read-only property implementations header = property(header) + schema = property(schema_name) %} } @@ -146,7 +144,15 @@ static const std::string& helper_fn_declaration_get_name(const IfcParse::declara return $self->data().id(); } - std::vector getAttributeNames() const { + int __len__() const { + if ($self->declaration().as_entity()) { + return $self->declaration().as_entity()->attribute_count(); + } else { + return 1; + } + } + + std::vector get_attribute_names() const { if (!$self->declaration().as_entity()) { return std::vector(1, "wrappedValue"); } @@ -164,7 +170,7 @@ static const std::string& helper_fn_declaration_get_name(const IfcParse::declara return attr_names; } - std::vector getInverseAttributeNames() const { + std::vector get_inverse_attribute_names() const { if (!$self->declaration().as_entity()) { return std::vector(0); } @@ -223,7 +229,13 @@ static const std::string& helper_fn_declaration_get_name(const IfcParse::declara } unsigned get_argument_index(const std::string& a) const { - return $self->declaration().as_entity()->attribute_index(a); + if ($self->declaration().as_entity()) { + return $self->declaration().as_entity()->attribute_index(a); + } else if (a == "wrappedValue") { + return 0; + } else { + throw IfcParse::IfcException(a + " not found on " + $self->declaration().name()); + } } IfcEntityList::ptr get_inverse(const std::string& a) { @@ -239,6 +251,22 @@ static const std::string& helper_fn_declaration_get_name(const IfcParse::declara throw IfcParse::IfcException(a + " not found on " + $self->declaration().name()); } + const char* const get_argument_type(unsigned int i) const { + IfcUtil::ArgumentType arg_type = IfcUtil::from_parameter_type($self->declaration().as_entity()->attribute_by_index(i)->type_of_attribute()); + return IfcUtil::ArgumentTypeToString(arg_type); + } + + const std::string& get_argument_name(unsigned int i) const { + if ($self->declaration().as_entity()) { + return $self->declaration().as_entity()->attribute_by_index(i)->name(); + } else if (i == 0) { + static std::string WRAPPED = "wrappedValue"; + return WRAPPED; + } else { + throw IfcParse::IfcException(boost::lexical_cast(i) + " out of bounds on " + $self->declaration().name()); + } + } + void setArgumentAsNull(unsigned int i) { bool is_optional = $self->declaration().as_entity()->attribute_by_index(i)->optional(); if (is_optional) { @@ -488,6 +516,7 @@ static const std::string& helper_fn_declaration_get_name(const IfcParse::declara %include "../ifcparse/IfcSpfHeader.h" %include "../ifcparse/IfcFile.h" %include "../ifcparse/IfcBaseClass.h" +%include "../ifcparse/IfcSchema.h" // The IfcFile* returned by open() is to be freed by SWIG/Python %newobject open; diff --git a/src/ifcwrap/IfcPython.i b/src/ifcwrap/IfcPython.i index a7f178da6c..2bdf89637c 100644 --- a/src/ifcwrap/IfcPython.i +++ b/src/ifcwrap/IfcPython.i @@ -78,6 +78,7 @@ #include "../ifcparse/Ifc4.h" #include "../ifcparse/IfcBaseClass.h" #include "../ifcparse/IfcFile.h" + #include "../ifcparse/IfcSchema.h" #include %} diff --git a/test/tests.py b/test/tests.py index d76e948e7c..d65b2f38ca 100644 --- a/test/tests.py +++ b/test/tests.py @@ -49,7 +49,7 @@ assert prop.NominalValue.wrappedValue in str(prop) # An instance added to a new file yields the same string # representation, except for any instance name identifiers. -f2 = ifcopenshell.open() +f2 = ifcopenshell.file(schema=f.schema) prop2 = f2.add(prop) assert str(prop) == str(prop2).replace(str(prop2.id()), str(prop.id())) assert prop2.id() == 1 @@ -61,7 +61,7 @@ assert f2.add(app).get_info(False, True) == app.get_info(False, True) assert "Version" in dir(app) # Enumeration of entity type names -g = ifcopenshell.open() +g = ifcopenshell.file(schema=f.schema) p = g.createIfcCartesianPoint((0.,0.)) assert len(g.types()) == 1 assert "IfcPoint" in g.types_with_super()