mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Rework memory mngmt
This commit is contained in:
@@ -71,7 +71,7 @@ except Exception as e:
|
||||
|
||||
from . import guid
|
||||
from .file import file
|
||||
from .entity_instance import entity_instance, register_schema_attributes
|
||||
from .entity_instance import entity_instance
|
||||
from .sql import sqlite, sqlite_entity
|
||||
try:
|
||||
from .stream import stream, stream_entity
|
||||
@@ -171,7 +171,7 @@ def create_entity(type, schema="IFC4", *args, **kwargs):
|
||||
model = ifcopenshell.file()
|
||||
model.add(person) # #1=IfcPerson($,$,$,$,$,$,$,$)
|
||||
"""
|
||||
e = entity_instance((schema, type))
|
||||
e = ifcopenshell_wrapper.make_instance(schema, type)
|
||||
attrs = list(enumerate(args)) + [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()]
|
||||
for idx, arg in attrs:
|
||||
e[idx] = arg
|
||||
@@ -195,7 +195,6 @@ def register_schema(schema):
|
||||
schema.schema.this.disown()
|
||||
schema.disown()
|
||||
ifcopenshell_wrapper.register_schema(schema.schema)
|
||||
register_schema_attributes(schema.schema)
|
||||
|
||||
|
||||
def schema_by_name(
|
||||
|
||||
@@ -42,66 +42,8 @@ except ImportError as e:
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def set_derived_attribute(*args):
|
||||
raise TypeError("Unable to set derived attribute")
|
||||
|
||||
|
||||
def set_unsupported_attribute(*args):
|
||||
raise TypeError("This is an unsupported attribute type")
|
||||
|
||||
|
||||
# For every schema and its entities populate a list
|
||||
# of functions for every entity attribute (including
|
||||
# inherited attributes) to set that particular
|
||||
# attribute by index.
|
||||
# For example. IFC2X3.IfcWall with have a list of
|
||||
# 9 methods. The first will point at
|
||||
# ifcopenshell.ifcopenshell_wrapper.entity_instance.setArgumentAsString
|
||||
# because the first attribute GlobalId ultimately
|
||||
# is of type string.
|
||||
# Previously, resolving the appropriate function was
|
||||
# done for each invocation of __setitem__. Now this
|
||||
# mapping is built once during initialization of the
|
||||
# module.
|
||||
_method_dict = {}
|
||||
|
||||
|
||||
def register_schema_attributes(schema):
|
||||
for decl in schema.declarations():
|
||||
if hasattr(decl, "argument_types"):
|
||||
fq_name = ".".join((schema.name(), decl.name()))
|
||||
|
||||
# get type strings as reported by IfcOpenShell C++
|
||||
type_strs = decl.argument_types()
|
||||
|
||||
# convert case for setter function
|
||||
type_strs = [x.title().replace(" ", "") for x in type_strs]
|
||||
|
||||
# binary and enumeration are passed from python as string as well
|
||||
type_strs = [x.replace("Binary", "String") for x in type_strs]
|
||||
type_strs = [x.replace("Enumeration", "String") for x in type_strs]
|
||||
|
||||
# prefix to get method names
|
||||
fn_names = ["setArgumentAs" + x for x in type_strs]
|
||||
|
||||
# resolve to actual functions in wrapper
|
||||
functions = [
|
||||
set_derived_attribute
|
||||
if mname == "setArgumentAsDerived"
|
||||
else set_unsupported_attribute
|
||||
if mname == "setArgumentAsUnknown"
|
||||
else getattr(ifcopenshell_wrapper.entity_instance, mname)
|
||||
for mname in fn_names
|
||||
]
|
||||
|
||||
_method_dict[fq_name] = functions
|
||||
|
||||
|
||||
for nm in ifcopenshell_wrapper.schema_names():
|
||||
schema = ifcopenshell_wrapper.schema_by_name(nm)
|
||||
register_schema_attributes(schema)
|
||||
|
||||
# poor man's enum
|
||||
ATTR_INVALID, ATTR_FORWARD, ATTR_INVERSE, ATTR_FORWARD_DERIVED = range(4)
|
||||
|
||||
class entity_instance(object):
|
||||
"""Base class for all IFC objects.
|
||||
@@ -120,44 +62,22 @@ class entity_instance(object):
|
||||
>>> #423=IfcProductDefinitionShape($,$,(#409,#421))
|
||||
"""
|
||||
|
||||
wrapped_data: ifcopenshell_wrapper.entity_instance
|
||||
|
||||
def __init__(self, e, file=None):
|
||||
if isinstance(e, tuple):
|
||||
e = ifcopenshell_wrapper.new_IfcBaseClass(*e)
|
||||
super(entity_instance, self).__setattr__("wrapped_data", e)
|
||||
super(entity_instance, self).__setattr__("method_list", None)
|
||||
|
||||
# Make sure the file is not gc'ed while we have live instances
|
||||
self.wrapped_data.file = file
|
||||
|
||||
def __del__(self):
|
||||
"""
|
||||
#2471 while the precise chain of action is unclear, creating
|
||||
instance references prevents file gc, even with all instance
|
||||
refs deleted. This is a work-around for that.
|
||||
"""
|
||||
self.wrapped_data.file = None
|
||||
|
||||
@property
|
||||
def file(self):
|
||||
# ugh circular imports, name collisions
|
||||
from . import file
|
||||
|
||||
return file.from_pointer(self.wrapped_data.file_pointer())
|
||||
return file.from_pointer(self.file_pointer())
|
||||
|
||||
def __getattr__(self, name):
|
||||
INVALID, FORWARD, INVERSE = range(3)
|
||||
attr_cat = self.wrapped_data.get_attribute_category(name)
|
||||
if attr_cat == FORWARD:
|
||||
idx = self.wrapped_data.get_argument_index(name)
|
||||
if _method_dict[self.is_a(True)][idx] != set_derived_attribute:
|
||||
# A bit ugly, but we fall through to derived attribute handling below
|
||||
return entity_instance.wrap_value(self.wrapped_data.get_argument(idx), self.wrapped_data.file)
|
||||
elif attr_cat == INVERSE:
|
||||
vs = entity_instance.wrap_value(self.wrapped_data.get_inverse(name), self.wrapped_data.file)
|
||||
attr_cat = self.get_attribute_category(name)
|
||||
if attr_cat == ATTR_FORWARD:
|
||||
idx = self.get_argument_index(name)
|
||||
return self.get_argument(idx)
|
||||
elif attr_cat == ATTR_INVERSE:
|
||||
vs = self.get_inverse(name)
|
||||
if settings.unpack_non_aggregate_inverses:
|
||||
schema_name = self.wrapped_data.is_a(True).split(".")[0]
|
||||
schema_name = self.is_a(True).split(".")[0]
|
||||
ent = ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(self.is_a())
|
||||
inv = [i for i in ent.all_inverse_attributes() if i.name() == name][0]
|
||||
if (inv.bound1(), inv.bound2()) == (-1, -1):
|
||||
@@ -166,9 +86,9 @@ class entity_instance(object):
|
||||
else:
|
||||
vs = None
|
||||
return vs
|
||||
|
||||
|
||||
# derived attribute perhaps?
|
||||
schema_name = self.wrapped_data.is_a(True).split(".")[0]
|
||||
schema_name = self.is_a(True).split(".")[0]
|
||||
try:
|
||||
rules = importlib.import_module(f"ifcopenshell.express.rules.{schema_name}")
|
||||
except:
|
||||
@@ -180,21 +100,21 @@ class entity_instance(object):
|
||||
subprocess.run([sys.executable, "-m", "ifcopenshell.express.rule_compiler", schema_path, fn], check=True)
|
||||
time.sleep(1.)
|
||||
rules = importlib.import_module(schema_name)
|
||||
|
||||
|
||||
def yield_supertypes():
|
||||
decl = ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(self.is_a())
|
||||
while decl:
|
||||
yield decl.name()
|
||||
decl = decl.supertype()
|
||||
|
||||
|
||||
for sty in yield_supertypes():
|
||||
fn = getattr(rules, f"calc_{sty}_{name}", None)
|
||||
if fn:
|
||||
return fn(self)
|
||||
|
||||
|
||||
if attr_cat != FORWARD:
|
||||
raise AttributeError(
|
||||
"entity instance of type '%s' has no attribute '%s'" % (self.wrapped_data.is_a(True), name)
|
||||
"entity instance of type '%s' has no attribute '%s'" % (self.is_a(True), name)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -235,26 +155,6 @@ class entity_instance(object):
|
||||
else:
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def wrap_value(v, file):
|
||||
def wrap(e):
|
||||
return entity_instance(e, file)
|
||||
|
||||
def is_instance(e):
|
||||
return isinstance(e, ifcopenshell_wrapper.entity_instance)
|
||||
|
||||
return entity_instance.walk(is_instance, wrap, v)
|
||||
|
||||
@staticmethod
|
||||
def unwrap_value(v):
|
||||
def unwrap(e):
|
||||
return e.wrapped_data
|
||||
|
||||
def is_instance(e):
|
||||
return isinstance(e, entity_instance)
|
||||
|
||||
return entity_instance.walk(is_instance, unwrap, v)
|
||||
|
||||
def attribute_type(self, attr: int) -> str:
|
||||
"""Return the data type of a positional attribute of the element
|
||||
|
||||
@@ -262,8 +162,8 @@ class entity_instance(object):
|
||||
:type attr: int
|
||||
:rtype: string
|
||||
"""
|
||||
attr_idx = attr if isinstance(attr, numbers.Integral) else self.wrapped_data.get_argument_index(attr)
|
||||
return self.wrapped_data.get_argument_type(attr_idx)
|
||||
attr_idx = attr if isinstance(attr, numbers.Integral) else self.get_argument_index(attr)
|
||||
return self.get_argument_type(attr_idx)
|
||||
|
||||
def attribute_name(self, attr_idx: int) -> str:
|
||||
"""Return the name of a positional attribute of the element
|
||||
@@ -272,37 +172,28 @@ class entity_instance(object):
|
||||
:type attr_idx: int
|
||||
:rtype: string
|
||||
"""
|
||||
return self.wrapped_data.get_argument_name(attr_idx)
|
||||
return self.get_argument_name(attr_idx)
|
||||
|
||||
def __setattr__(self, key: str, value: Any) -> None:
|
||||
index = self.wrapped_data.get_argument_index(key)
|
||||
index = self.get_argument_index(key)
|
||||
self[index] = value
|
||||
|
||||
def __getitem__(self, key: int) -> Any:
|
||||
if key < 0 or key >= len(self):
|
||||
raise IndexError("Attribute index {} out of range for instance of type {}".format(key, self.is_a()))
|
||||
return entity_instance.wrap_value(self.wrapped_data.get_argument(key), self.wrapped_data.file)
|
||||
return self.get_argument(key)
|
||||
|
||||
def __setitem__(self, idx: int, value: T) -> T:
|
||||
if self.wrapped_data.file and self.wrapped_data.file.transaction:
|
||||
self.wrapped_data.file.transaction.store_edit(self, idx, value)
|
||||
|
||||
if self.method_list is None:
|
||||
super(entity_instance, self).__setattr__("method_list", _method_dict[self.is_a(True)])
|
||||
|
||||
method = self.method_list[idx]
|
||||
|
||||
if value is None:
|
||||
if method is not set_derived_attribute:
|
||||
self.wrapped_data.setArgumentAsNull(idx)
|
||||
else:
|
||||
self.method_list[idx](self.wrapped_data, idx, entity_instance.unwrap_value(value))
|
||||
|
||||
if self.file and self.file.transaction:
|
||||
self.file.transaction.store_edit(self, idx, value)
|
||||
|
||||
self.setAttribute(idx, value)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return len(self.wrapped_data)
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.wrapped_data)
|
||||
|
||||
@@ -315,7 +206,7 @@ class entity_instance(object):
|
||||
the specific control directives.
|
||||
"""
|
||||
|
||||
return self.wrapped_data.to_string(valid_spf)
|
||||
return self.to_string(valid_spf)
|
||||
|
||||
@overload
|
||||
def is_a(self) -> str: ...
|
||||
@@ -348,19 +239,19 @@ class entity_instance(object):
|
||||
f.is_a('IfcPerson')
|
||||
>>> True
|
||||
"""
|
||||
return self.wrapped_data.is_a(*args)
|
||||
return self.is_a(*args)
|
||||
|
||||
def id(self) -> int:
|
||||
"""Return the STEP numerical identifier
|
||||
|
||||
:rtype: int
|
||||
"""
|
||||
return self.wrapped_data.id()
|
||||
return self.id()
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(self, type(other)):
|
||||
return False
|
||||
elif None in (self.wrapped_data.file, other.wrapped_data.file):
|
||||
elif None in (self.file, other.file):
|
||||
# when not added to a file, we can only compare attribute values
|
||||
# and we need this for where rule evaluation
|
||||
return self.get_info(recursive=True, include_identifier=False) == other.get_info(
|
||||
@@ -373,10 +264,10 @@ class entity_instance(object):
|
||||
if self.id():
|
||||
return self.wrapped_data == other.wrapped_data
|
||||
else:
|
||||
return (self.is_a(), self[0], self.wrapped_data.file_pointer()) == (
|
||||
return (self.is_a(), self[0], self.file_pointer()) == (
|
||||
other.is_a(),
|
||||
other[0],
|
||||
other.wrapped_data.file_pointer(),
|
||||
other.file_pointer(),
|
||||
)
|
||||
|
||||
def is_entity(self) -> bool:
|
||||
@@ -385,7 +276,7 @@ class entity_instance(object):
|
||||
Returns:
|
||||
bool: True if the instance is an entity
|
||||
"""
|
||||
schema_name = self.wrapped_data.is_a(True).split(".")[0]
|
||||
schema_name = self.is_a(True).split(".")[0]
|
||||
decl = ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(self.is_a())
|
||||
return isinstance(decl, ifcopenshell_wrapper.entity)
|
||||
|
||||
@@ -459,17 +350,17 @@ class entity_instance(object):
|
||||
# step id. Selected type instances (such as IfcPropertySingleValue.NominalValue
|
||||
# always have id=0, so we hash <type, value, file pointer>
|
||||
if self.id():
|
||||
return hash((self.id(), self.wrapped_data.file_pointer()))
|
||||
return hash((self.id(), self.file_pointer()))
|
||||
else:
|
||||
return hash((self.is_a(), self[0], self.wrapped_data.file_pointer()))
|
||||
return hash((self.is_a(), self[0], self.file_pointer()))
|
||||
|
||||
def __dir__(self):
|
||||
return sorted(
|
||||
set(
|
||||
itertools.chain(
|
||||
dir(type(self)),
|
||||
map(str, self.wrapped_data.get_attribute_names()),
|
||||
map(str, self.wrapped_data.get_inverse_attribute_names()),
|
||||
map(str, self.get_attribute_names()),
|
||||
map(str, self.get_inverse_attribute_names()),
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -513,7 +404,7 @@ class entity_instance(object):
|
||||
logging.exception("unhandled exception while getting id / type info on {}".format(self))
|
||||
for i in range(len(self)):
|
||||
try:
|
||||
if self.wrapped_data.get_attribute_names()[i] in ignore:
|
||||
if self.get_attribute_names()[i] in ignore:
|
||||
continue
|
||||
attr_value = self[i]
|
||||
|
||||
@@ -548,7 +439,8 @@ class entity_instance(object):
|
||||
|
||||
return return_type(_())
|
||||
|
||||
__dict__ = property(get_info)
|
||||
# @todo this is no longer possible with the direct integration into the swig type
|
||||
# __dict__ = property(get_info)
|
||||
|
||||
def get_info_2(self, include_identifier=True, recursive=False, return_type=dict, ignore=()):
|
||||
"""More perfomant version of `.get_info()` but with limited arguments values.\n
|
||||
|
||||
@@ -328,7 +328,7 @@ class file(object):
|
||||
"""
|
||||
eid = kwargs.pop("id", -1)
|
||||
|
||||
e = entity_instance((self.schema_identifier, type), self)
|
||||
e = ifcopenshell_wrapper.make_instance(self.schema_identifier, type)
|
||||
|
||||
# Create pairs of {attribute index, attribute value}.
|
||||
# Keyword arguments are mapped to their corresponding
|
||||
@@ -337,7 +337,7 @@ class file(object):
|
||||
# @todo we should probably check that values for
|
||||
# attributes are not passed as duplicates using
|
||||
# both regular arguments and keyword arguments.
|
||||
attrs = list(enumerate(args)) + [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()]
|
||||
attrs = list(enumerate(args)) + [(e.get_argument_index(name), arg) for name, arg in kwargs.items()]
|
||||
|
||||
# Don't store these attributes as transactions
|
||||
# as the creation it self is already stored with
|
||||
@@ -353,14 +353,13 @@ class file(object):
|
||||
if attrs:
|
||||
self.transaction = transaction
|
||||
|
||||
# Once the values are populated add the instance
|
||||
# Once the values are populated move the instance
|
||||
# to the file.
|
||||
self.wrapped_data.add(e.wrapped_data, eid)
|
||||
e = self.wrapped_data.add(e, eid)
|
||||
|
||||
# The file container now handles the lifetime of
|
||||
# this instance. Tell SWIG that it is no longer
|
||||
# the owner.
|
||||
e.wrapped_data.this.disown()
|
||||
|
||||
if self.transaction:
|
||||
self.transaction.store_create(e)
|
||||
@@ -398,9 +397,9 @@ class file(object):
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, numbers.Integral):
|
||||
return entity_instance(self.wrapped_data.by_id(key), self)
|
||||
return self.wrapped_data.by_id(key)
|
||||
elif isinstance(key, basestring):
|
||||
return entity_instance(self.wrapped_data.by_guid(str(key)), self)
|
||||
return self.wrapped_data.by_guid(str(key))
|
||||
|
||||
def by_id(self, id: int) -> ifcopenshell.entity_instance:
|
||||
"""Return an IFC entity instance filtered by IFC ID.
|
||||
@@ -441,8 +440,8 @@ class file(object):
|
||||
|
||||
if self.transaction:
|
||||
max_id = self.wrapped_data.getMaxId()
|
||||
inst.wrapped_data.this.disown()
|
||||
result = entity_instance(self.wrapped_data.add(inst.wrapped_data, -1 if _id is None else _id), self)
|
||||
# inst.wrapped_data.this.disown()
|
||||
result = self.wrapped_data.add(inst, -1 if _id is None else _id)
|
||||
if self.transaction:
|
||||
added_elements = [e for e in self.traverse(result) if e.id() > max_id]
|
||||
[self.transaction.store_create(e) for e in reversed(added_elements)]
|
||||
@@ -461,8 +460,8 @@ class file(object):
|
||||
:rtype: list[ifcopenshell.entity_instance.entity_instance]
|
||||
"""
|
||||
if include_subtypes:
|
||||
return [entity_instance(e, self) for e in self.wrapped_data.by_type(type)]
|
||||
return [entity_instance(e, self) for e in self.wrapped_data.by_type_excl_subtypes(type)]
|
||||
return self.wrapped_data.by_type(type)
|
||||
return self.wrapped_data.by_type_excl_subtypes(type)
|
||||
|
||||
def traverse(
|
||||
self, inst: ifcopenshell.entity_instance, max_levels=None, breadth_first=False
|
||||
@@ -486,7 +485,7 @@ class file(object):
|
||||
else:
|
||||
fn = self.wrapped_data.traverse
|
||||
|
||||
return [entity_instance(e, self) for e in fn(inst.wrapped_data, max_levels)]
|
||||
return fn(inst, max_levels)
|
||||
|
||||
def get_inverse(
|
||||
self, inst: ifcopenshell.entity_instance, allow_duplicate=False, with_attribute_indices=False
|
||||
@@ -504,11 +503,11 @@ class file(object):
|
||||
if with_attribute_indices and not allow_duplicate:
|
||||
raise ValueError("with_attribute_indices requires allow_duplicate to be True")
|
||||
|
||||
inverses = [entity_instance(e, self) for e in self.wrapped_data.get_inverse(inst.wrapped_data)]
|
||||
inverses = self.wrapped_data.get_inverse(inst)
|
||||
|
||||
if allow_duplicate:
|
||||
if with_attribute_indices:
|
||||
idxs = self.wrapped_data.get_inverse_indices(inst.wrapped_data)
|
||||
idxs = self.wrapped_data.get_inverse_indices(inst)
|
||||
return list(zip(inverses, idxs))
|
||||
else:
|
||||
return inverses
|
||||
@@ -523,7 +522,7 @@ class file(object):
|
||||
:returns: The total number of references
|
||||
:rtype: int
|
||||
"""
|
||||
return self.wrapped_data.get_total_inverses(inst.wrapped_data)
|
||||
return self.wrapped_data.get_total_inverses(inst)
|
||||
|
||||
def remove(self, inst: ifcopenshell.entity_instance) -> None:
|
||||
"""Deletes an IFC object in the file.
|
||||
@@ -538,7 +537,7 @@ class file(object):
|
||||
"""
|
||||
if self.transaction:
|
||||
self.transaction.store_delete(inst)
|
||||
return self.wrapped_data.remove(inst.wrapped_data)
|
||||
return self.wrapped_data.remove(inst)
|
||||
|
||||
def batch(self):
|
||||
"""Low-level mechanism to speed up deletion of large subgraphs"""
|
||||
@@ -605,4 +604,4 @@ class file(object):
|
||||
|
||||
@staticmethod
|
||||
def from_pointer(v):
|
||||
return file_dict.get(v)()
|
||||
return file_dict.get(v)() if v else None
|
||||
|
||||
@@ -1902,8 +1902,6 @@ IfcFile::instance_storage_type IfcFile::addEntity(IfcUtil::IfcBaseClass* entity,
|
||||
return mit->second;
|
||||
}
|
||||
|
||||
instance_storage_type new_entity = instance_storage_type(entity);
|
||||
|
||||
// Obtain all forward references by a depth-first
|
||||
// traversal and add them to the file.
|
||||
if (parsing_complete_) {
|
||||
@@ -1922,12 +1920,17 @@ IfcFile::instance_storage_type IfcFile::addEntity(IfcUtil::IfcBaseClass* entity,
|
||||
}
|
||||
}
|
||||
|
||||
instance_storage_type new_entity;
|
||||
|
||||
// See whether the instance is already part of a file
|
||||
if (entity->data().file != 0) {
|
||||
if (entity->data().file == 0) {
|
||||
// We can simply wrap it into a new smart pointer
|
||||
new_entity = instance_storage_type(entity);
|
||||
} else {
|
||||
if (entity->data().file == this) {
|
||||
if (entity->declaration().as_entity() == nullptr) {
|
||||
// While not a mapping that can be queried, we do need to free the instance later on
|
||||
byidentity_[new_entity->identity()] = new_entity;
|
||||
byidentity_[entity->identity()] = instance_storage_type(entity);
|
||||
}
|
||||
|
||||
// If it is part of this file
|
||||
|
||||
@@ -44,6 +44,10 @@ INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS})
|
||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
SET(CMAKE_SWIG_FLAGS ${SWIG_DEFINES})
|
||||
|
||||
# track file modifications to reinvoke swig wrapper code gen
|
||||
set(USE_SWIG_DEPENDENCIES TRUE)
|
||||
|
||||
# NOTE Workaround for most likely missing debug Python libraries on Windows (requires Python built from the source).
|
||||
# Python 3.5 intaller and onwards will have an option to install the debug libraries too.
|
||||
# NOTE PYTHON_DEBUG_LIBRARIES appears to be a deprecated variable
|
||||
@@ -68,6 +72,15 @@ endif()
|
||||
# directory in which the wrapper can be installed.
|
||||
FIND_PACKAGE(PythonInterp)
|
||||
IF((PYTHONINTERP_FOUND AND NOT "${PYTHON_EXECUTABLE}" STREQUAL "") OR PYTHON_MODULE_INSTALL_DIR)
|
||||
# Find Python interpreter and get its version
|
||||
execute_process(
|
||||
COMMAND ${PYTHON_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))"
|
||||
OUTPUT_VARIABLE PYTHON_EXTENSION_SUFFIX
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
set_target_properties(${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME} PROPERTIES SUFFIX ${PYTHON_EXTENSION_SUFFIX})
|
||||
|
||||
if (PYTHON_MODULE_INSTALL_DIR)
|
||||
set(python_package_dir "${PYTHON_MODULE_INSTALL_DIR}")
|
||||
else()
|
||||
|
||||
@@ -587,13 +587,6 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
|
||||
%}
|
||||
};
|
||||
|
||||
%extend IfcGeom::Matrix {
|
||||
%pythoncode %{
|
||||
# Hide the getters with read-only property implementations
|
||||
data = property(data)
|
||||
%}
|
||||
};
|
||||
|
||||
%{
|
||||
template <typename T>
|
||||
std::string to_locale_invariant_string(const T& t) {
|
||||
|
||||
+378
-231
@@ -50,10 +50,15 @@ private:
|
||||
%ignore IfcParse::IfcFile::instances_by_type;
|
||||
%ignore IfcParse::IfcFile::instances_by_type_excl_subtypes;
|
||||
|
||||
%rename("file") IfcFile;
|
||||
%ignore IfcParse::IfcFile::addEntity;
|
||||
%ignore IfcParse::IfcFile::addEntities;
|
||||
%ignore IfcParse::IfcFile::removeEntity;
|
||||
|
||||
%ignore IfcParse::IfcFile;
|
||||
%ignore IfcUtil::IfcBaseClass;
|
||||
%ignore IfcUtil::IfcBaseEntity;
|
||||
%ignore IfcUtil::IfcBaseInterface;
|
||||
|
||||
class attribute_value_derived {};
|
||||
%{
|
||||
class attribute_value_derived {};
|
||||
@@ -87,65 +92,190 @@ PyObject* get_feature(const std::string& x) {
|
||||
|
||||
%}
|
||||
|
||||
%ignore entity_instance::entity_instance;
|
||||
|
||||
%{
|
||||
class entity_instance;
|
||||
const std::string& helper_fn_declaration_get_name(const IfcParse::declaration* decl);
|
||||
IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsigned i);
|
||||
bool check_aggregate_of_type(PyObject* aggregate, void* type_obj);
|
||||
bool check_aggregate_of_aggregate_of_type(PyObject* aggregate, void* type_obj);
|
||||
%}
|
||||
|
||||
%pythoncode %{
|
||||
### hack hack hack
|
||||
### we trick swig into inheriting from our own extension class
|
||||
### that way we do not constantly need to decorate/undecorate
|
||||
# @todo is there no official way to do this?
|
||||
_old_object = _object
|
||||
from .entity_instance import entity_instance as custom_base
|
||||
_object = custom_base
|
||||
%}
|
||||
|
||||
%include "utils/type_conversion.i"
|
||||
%include "utils/typemaps_in.i"
|
||||
%include "utils/typemaps_out.i"
|
||||
|
||||
%inline %{
|
||||
class entity_instance {
|
||||
boost::variant<
|
||||
IfcParse::IfcFile::instance_storage_type,
|
||||
std::weak_ptr<IfcUtil::IfcBaseClass>
|
||||
IfcUtil::IfcBaseClass*,
|
||||
std::weak_ptr<IfcUtil::IfcBaseClass>,
|
||||
boost::blank
|
||||
> data_;
|
||||
std::shared_ptr<IfcParse::IfcFile> file_;
|
||||
|
||||
struct visitor {
|
||||
IfcParse::IfcFile::instance_storage_type operator()(const IfcParse::IfcFile::instance_storage_type& t) {
|
||||
const IfcUtil::IfcBaseClass* const operator()(const IfcUtil::IfcBaseClass* const & t) {
|
||||
return t;
|
||||
}
|
||||
IfcParse::IfcFile::instance_storage_type operator()(const std::weak_ptr<IfcUtil::IfcBaseClass>& t) {
|
||||
const IfcUtil::IfcBaseClass* const operator()(const std::weak_ptr<IfcUtil::IfcBaseClass>& t) {
|
||||
auto u = t.lock();
|
||||
if (u) {
|
||||
return u;
|
||||
// @todo this is not really safe, nor ideal as we don't retain the scope
|
||||
// until used.
|
||||
return &*u;
|
||||
} else {
|
||||
throw std::runtime_error("No longer availabe");
|
||||
}
|
||||
}
|
||||
IfcUtil::IfcBaseClass* operator()(boost::blank) {
|
||||
throw std::runtime_error("No longer availabe");
|
||||
}
|
||||
IfcUtil::IfcBaseClass* operator()(IfcUtil::IfcBaseClass* & t) {
|
||||
return t;
|
||||
}
|
||||
IfcUtil::IfcBaseClass* operator()(std::weak_ptr<IfcUtil::IfcBaseClass>& t) {
|
||||
auto u = t.lock();
|
||||
if (u) {
|
||||
// @todo this is not really safe, nor ideal as we don't retain the scope
|
||||
// until used.
|
||||
return &*u;
|
||||
} else {
|
||||
throw std::runtime_error("No longer availabe");
|
||||
}
|
||||
}
|
||||
};
|
||||
public:
|
||||
entity_instance(const IfcParse::IfcFile::instance_storage_type& shared)
|
||||
entity_instance(
|
||||
const IfcParse::IfcFile::instance_storage_type& shared,
|
||||
const std::shared_ptr<IfcParse::IfcFile>& file
|
||||
)
|
||||
: data_(std::weak_ptr<IfcUtil::IfcBaseClass>(shared))
|
||||
{}
|
||||
, file_(file)
|
||||
{
|
||||
std::cout << "init " << this << " " << data_.which() << std::endl;
|
||||
}
|
||||
|
||||
entity_instance(IfcUtil::IfcBaseClass* naked)
|
||||
: data_(IfcParse::IfcFile::instance_storage_type(naked))
|
||||
{}
|
||||
: data_(naked)
|
||||
{
|
||||
std::cout << "init " << this << " " << data_.which() << std::endl;
|
||||
}
|
||||
|
||||
operator IfcUtil::IfcBaseClass*() const {
|
||||
return &*boost::apply_visitor(visitor{}, data_);
|
||||
~entity_instance() {
|
||||
if (data_.which() == 0) {
|
||||
delete (const IfcUtil::IfcBaseClass*)*this;
|
||||
}
|
||||
}
|
||||
|
||||
IfcUtil::IfcBaseClass* move() {
|
||||
auto old = i();
|
||||
data_ = boost::blank{};
|
||||
std::cout << "reset " << this << " " << data_.which() << std::endl;
|
||||
return old;
|
||||
}
|
||||
|
||||
entity_instance(const entity_instance&) = delete;
|
||||
entity_instance& operator=(const entity_instance&) = delete;
|
||||
|
||||
operator const IfcUtil::IfcBaseClass*() const {
|
||||
std::cout << "visit" << this << std::endl;
|
||||
return boost::apply_visitor(visitor{}, data_);
|
||||
}
|
||||
|
||||
const IfcUtil::IfcBaseClass* i() const {
|
||||
std::cout << "visit" << this << std::endl;
|
||||
return boost::apply_visitor(visitor{}, data_);
|
||||
}
|
||||
|
||||
operator IfcUtil::IfcBaseClass*() {
|
||||
std::cout << "visit" << this << std::endl;
|
||||
return boost::apply_visitor(visitor{}, data_);
|
||||
}
|
||||
|
||||
IfcUtil::IfcBaseClass* i() {
|
||||
std::cout << "visit" << this << std::endl;
|
||||
return boost::apply_visitor(visitor{}, data_);
|
||||
}
|
||||
|
||||
const IfcParse::declaration& declaration() const {
|
||||
std::cout << "visit" << this << std::endl;
|
||||
return boost::apply_visitor(visitor{}, data_)->declaration();
|
||||
}
|
||||
|
||||
const IfcEntityInstanceData& data() const {
|
||||
const IfcEntityInstanceData& data() const {
|
||||
std::cout << "visit" << this << std::endl;
|
||||
return boost::apply_visitor(visitor{}, data_)->data();
|
||||
}
|
||||
|
||||
IfcEntityInstanceData& data() {
|
||||
IfcEntityInstanceData& data() {
|
||||
std::cout << "visit" << this << std::endl;
|
||||
return boost::apply_visitor(visitor{}, data_)->data();
|
||||
}
|
||||
|
||||
uint32_t identity() const {
|
||||
std::cout << "visit" << this << std::endl;
|
||||
return boost::apply_visitor(visitor{}, data_)->identity();
|
||||
}
|
||||
|
||||
const std::shared_ptr<IfcParse::IfcFile>& file_pointer() const {
|
||||
return file_;
|
||||
}
|
||||
};
|
||||
|
||||
%}
|
||||
|
||||
%pythoncode %{
|
||||
### hack hack hack
|
||||
### restore
|
||||
_object = _old_object
|
||||
%}
|
||||
|
||||
%inline %{
|
||||
// Using swig's builtin shared_ptr() feature does not work,
|
||||
// because we wouldn't actually get access to this pointer
|
||||
// in extend() calls, which is need to propagate the shared_ptr
|
||||
// control block to the references to it in dependent instances.
|
||||
class file {
|
||||
private:
|
||||
std::shared_ptr<IfcParse::IfcFile> ptr_;
|
||||
|
||||
public:
|
||||
file()
|
||||
: ptr_(new IfcParse::IfcFile)
|
||||
{}
|
||||
|
||||
IfcParse::IfcFile& operator* ()
|
||||
{
|
||||
return *ptr_;
|
||||
}
|
||||
|
||||
IfcParse::IfcFile* operator-> ()
|
||||
{
|
||||
return &*ptr_;
|
||||
}
|
||||
|
||||
operator const std::shared_ptr<IfcParse::IfcFile>&() const {
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
std::string __repr__() const {
|
||||
return "ifcopenshell.file object at " + std::to_string(reinterpret_cast<size_t>(ptr_.get()));
|
||||
}
|
||||
};
|
||||
%}
|
||||
|
||||
%{
|
||||
|
||||
const std::string& helper_fn_declaration_get_name(const IfcParse::declaration* decl) {
|
||||
@@ -174,100 +304,102 @@ IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsi
|
||||
}
|
||||
%}
|
||||
|
||||
%extend IfcParse::IfcFile {
|
||||
%extend file {
|
||||
// Use to correlate to entity_instance.file_pointer, so that we
|
||||
// can trace file ownership of instances on the python side.
|
||||
size_t file_pointer() const {
|
||||
return reinterpret_cast<size_t>($self);
|
||||
size_t file_pointer() {
|
||||
return reinterpret_cast<size_t>(&**self);
|
||||
}
|
||||
|
||||
entity_instance by_guid(const std::string& guid) {
|
||||
return $self->instance_by_guid(guid);
|
||||
entity_instance* by_guid(const std::string& guid) {
|
||||
return new entity_instance((*$self)->instance_by_guid(guid), (*$self));
|
||||
}
|
||||
|
||||
entity_instance by_id(int i) {
|
||||
return $self->instance_by_id_2(i);
|
||||
entity_instance* by_id(int i) {
|
||||
return new entity_instance((*$self)->instance_by_id_2(i), (*$self));
|
||||
}
|
||||
|
||||
std::vector<entity_instance> by_type(const std::string& ty) {
|
||||
std::vector<entity_instance> vec;
|
||||
auto range = $self->instances_by_type_range(ty);
|
||||
std::vector<entity_instance*> by_type(const std::string& ty) {
|
||||
std::vector<entity_instance*> vec;
|
||||
auto range = (*$self)->instances_by_type_range(ty);
|
||||
for (auto& inst : boost::make_iterator_range(range)) {
|
||||
vec.push_back(inst);
|
||||
vec.push_back(new entity_instance(inst, *$self));
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::vector<entity_instance> by_type_excl_subtypes(const std::string& ty) {
|
||||
std::vector<entity_instance> vec;
|
||||
auto range = $self->instances_by_type_excl_subtypes_range(ty);
|
||||
std::vector<entity_instance*> by_type_excl_subtypes(const std::string& ty) {
|
||||
std::vector<entity_instance*> vec;
|
||||
auto range = (*$self)->instances_by_type_excl_subtypes_range(ty);
|
||||
for (auto& inst : boost::make_iterator_range(range)) {
|
||||
vec.push_back(inst);
|
||||
vec.push_back(new entity_instance(inst, *$self));
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
entity_instance add(entity_instance& e, int i) {
|
||||
return $self->addEntity(e, i);
|
||||
entity_instance* add(entity_instance* e, int i) {
|
||||
return new entity_instance((*$self)->addEntity(e->move(), i), *self);
|
||||
}
|
||||
|
||||
void remove(entity_instance& e) {
|
||||
return $self->removeEntity(e);
|
||||
void remove(entity_instance* e) {
|
||||
return (*$self)->removeEntity(*e);
|
||||
}
|
||||
|
||||
aggregate_of_instance::ptr get_inverse(entity_instance& e) {
|
||||
return $self->getInverse(e.data().id(), 0, -1);
|
||||
aggregate_of_instance::ptr get_inverse(entity_instance* e) {
|
||||
return (*$self)->getInverse(e->data().id(), 0, -1);
|
||||
}
|
||||
|
||||
std::vector<int> get_inverse_indices(entity_instance& e) {
|
||||
return $self->get_inverse_indices(e.data().id());
|
||||
std::vector<int> get_inverse_indices(entity_instance* e) {
|
||||
return (*$self)->get_inverse_indices(e->data().id());
|
||||
}
|
||||
|
||||
int get_total_inverses(entity_instance& e) {
|
||||
return $self->getTotalInverses(e.data().id());
|
||||
int get_total_inverses(entity_instance* e) {
|
||||
return (*$self)->getTotalInverses(e->data().id());
|
||||
}
|
||||
|
||||
void write(const std::string& fn) {
|
||||
std::ofstream f(IfcUtil::path::from_utf8(fn).c_str());
|
||||
f << (*$self);
|
||||
f << (**$self);
|
||||
}
|
||||
|
||||
std::string to_string() {
|
||||
std::stringstream s;
|
||||
s << (*$self);
|
||||
s << (**$self);
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::vector<unsigned> entity_names() const {
|
||||
std::vector<unsigned> entity_names() {
|
||||
std::vector<unsigned> keys;
|
||||
keys.reserve(std::distance($self->begin(), $self->end()));
|
||||
for (IfcParse::IfcFile::entity_by_id_t::const_iterator it = $self->begin(); it != $self->end(); ++ it) {
|
||||
keys.reserve(std::distance((*$self)->begin(), (*$self)->end()));
|
||||
for (IfcParse::IfcFile::entity_by_id_t::const_iterator it = (*$self)->begin(); it != (*$self)->end(); ++ it) {
|
||||
keys.push_back(it->first);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
std::vector<std::string> types() const {
|
||||
const size_t n = std::distance($self->types_begin(), $self->types_end());
|
||||
std::vector<std::string> types() {
|
||||
const size_t n = std::distance((*$self)->types_begin(), (*$self)->types_end());
|
||||
std::vector<std::string> ts;
|
||||
ts.reserve(n);
|
||||
std::transform($self->types_begin(), $self->types_end(), std::back_inserter(ts), helper_fn_declaration_get_name);
|
||||
std::transform((*$self)->types_begin(), (*$self)->types_end(), std::back_inserter(ts), helper_fn_declaration_get_name);
|
||||
return ts;
|
||||
}
|
||||
|
||||
std::vector<std::string> types_with_super() const {
|
||||
const size_t n = std::distance($self->types_incl_super_begin(), $self->types_incl_super_end());
|
||||
std::vector<std::string> types_with_super() {
|
||||
const size_t n = std::distance((*$self)->types_incl_super_begin(), (*$self)->types_incl_super_end());
|
||||
std::vector<std::string> ts;
|
||||
ts.reserve(n);
|
||||
std::transform($self->types_incl_super_begin(), $self->types_incl_super_end(), std::back_inserter(ts), helper_fn_declaration_get_name);
|
||||
std::transform((*$self)->types_incl_super_begin(), (*$self)->types_incl_super_end(), std::back_inserter(ts), helper_fn_declaration_get_name);
|
||||
return ts;
|
||||
}
|
||||
|
||||
std::string schema_name() const {
|
||||
if ($self->schema() == 0) return "";
|
||||
return $self->schema()->name();
|
||||
std::string schema_name() {
|
||||
if ((*$self)->schema() == 0) return "";
|
||||
return (*$self)->schema()->name();
|
||||
}
|
||||
|
||||
IfcParse::IfcSpfHeader& header() { return (*$self)->header(); }
|
||||
|
||||
%pythoncode %{
|
||||
# Hide the getters with read-only property implementations
|
||||
header = property(header)
|
||||
@@ -287,6 +419,10 @@ IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsi
|
||||
std::vector<const IfcParse::attribute*>::const_iterator it = attrs.begin();
|
||||
for (; it != attrs.end(); ++it) {
|
||||
if ((*it)->name() == name) {
|
||||
if (self->declaration().as_entity()->derived()[std::distance(attrs.begin(), it)]) {
|
||||
// derived
|
||||
return 3;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -368,13 +504,13 @@ IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsi
|
||||
return t;
|
||||
}
|
||||
|
||||
std::pair<IfcUtil::ArgumentType,Argument*> get_argument(unsigned i) {
|
||||
return std::pair<IfcUtil::ArgumentType,Argument*>($self->data().getArgument(i)->type(), $self->data().getArgument(i));
|
||||
std::tuple<IfcUtil::ArgumentType, Argument*, entity_instance*> get_argument(unsigned i) {
|
||||
return { $self->data().getArgument(i)->type(), $self->data().getArgument(i), self };
|
||||
}
|
||||
|
||||
std::pair<IfcUtil::ArgumentType,Argument*> get_argument(const std::string& a) {
|
||||
std::tuple<IfcUtil::ArgumentType, Argument*, entity_instance*> get_argument(const std::string& a) {
|
||||
unsigned i = $self->declaration().as_entity()->attribute_index(a);
|
||||
return std::pair<IfcUtil::ArgumentType,Argument*>($self->data().getArgument(i)->type(), $self->data().getArgument(i));
|
||||
return { $self->data().getArgument(i)->type(), $self->data().getArgument(i), self };
|
||||
}
|
||||
|
||||
bool __eq__(entity_instance* other) const {
|
||||
@@ -430,202 +566,213 @@ IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsi
|
||||
void setAttribute(unsigned int i, PyObject* obj) {
|
||||
bool is_optional = $self->declaration().as_entity()->attribute_by_index(i)->optional();
|
||||
|
||||
if ()
|
||||
if (is_optional) {
|
||||
self->data().setArgument(i, new IfcWrite::IfcWriteArgument());
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
}
|
||||
|
||||
void setArgumentAsInt(unsigned int i, int v) {
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_INT) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v);
|
||||
self->data().setArgument(i, arg);
|
||||
} else if ( (arg_type == IfcUtil::Argument_BOOL) && ( (v == 0) || (v == 1) ) ) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v == 1);
|
||||
self->data().setArgument(i, arg);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
|
||||
void setArgumentAsBool(unsigned int i, bool v) {
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_BOOL) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v);
|
||||
self->data().setArgument(i, arg);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
|
||||
void setArgumentAsLogical(unsigned int i, boost::logic::tribool v) {
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_LOGICAL) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v);
|
||||
self->data().setArgument(i, arg);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
|
||||
void setArgumentAsDouble(unsigned int i, double v) {
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_DOUBLE) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v);
|
||||
self->data().setArgument(i, arg);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
|
||||
void setArgumentAsString(unsigned int i, const std::string& a) {
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_STRING) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(a);
|
||||
self->data().setArgument(i, arg);
|
||||
} else if (arg_type == IfcUtil::Argument_ENUMERATION) {
|
||||
const IfcParse::enumeration_type* enum_type = $self->declaration().schema()->declaration_by_name($self->declaration().type())->as_entity()->
|
||||
attribute_by_index(i)->type_of_attribute()->as_named_type()->declared_type()->as_enumeration_type();
|
||||
|
||||
std::vector<std::string>::const_iterator it = std::find(
|
||||
enum_type->enumeration_items().begin(),
|
||||
enum_type->enumeration_items().end(),
|
||||
a);
|
||||
|
||||
if (it == enum_type->enumeration_items().end()) {
|
||||
throw IfcParse::IfcException(a + " does not name a valid item for " + enum_type->name());
|
||||
}
|
||||
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(IfcWrite::IfcWriteArgument::EnumerationReference(it - enum_type->enumeration_items().begin(), it->c_str()));
|
||||
self->data().setArgument(i, arg);
|
||||
} else if (arg_type == IfcUtil::Argument_BINARY) {
|
||||
if (IfcUtil::valid_binary_string(a)) {
|
||||
boost::dynamic_bitset<> bits(a);
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(bits);
|
||||
self->data().setArgument(i, arg);
|
||||
if (obj == Py_None) {
|
||||
if (is_optional) {
|
||||
self->i()->unset_value(i);
|
||||
return;
|
||||
} else {
|
||||
throw IfcParse::IfcException("String not a valid binary representation");
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
|
||||
void setArgumentAsAggregateOfInt(unsigned int i, const std::vector<int>& v) {
|
||||
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_INT) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v);
|
||||
self->data().setArgument(i, arg);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
|
||||
if (PyLong_Check(obj)) {
|
||||
long v = PyLong_AsLong(obj);
|
||||
if (arg_type == IfcUtil::Argument_INT) {
|
||||
self->i()->set_value<int>(i, v);
|
||||
} else if ( (arg_type == IfcUtil::Argument_BOOL) && ( (v == 0) || (v == 1) ) ) {
|
||||
self->i()->set_value<int>(i, v == 1);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setArgumentAsAggregateOfDouble(unsigned int i, const std::vector<double>& v) {
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_DOUBLE) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v);
|
||||
self->data().setArgument(i, arg);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
else if (PyBool_Check(obj)) {
|
||||
bool v = PyObject_IsTrue(obj);
|
||||
if (arg_type == IfcUtil::Argument_BOOL || arg_type == IfcUtil::Argument_LOGICAL) {
|
||||
self->i()->set_value(i, v);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setArgumentAsAggregateOfString(unsigned int i, const std::vector<std::string>& v) {
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_STRING) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v);
|
||||
self->data().setArgument(i, arg);
|
||||
} else if (arg_type == IfcUtil::Argument_AGGREGATE_OF_BINARY) {
|
||||
std::vector< boost::dynamic_bitset<> > bits;
|
||||
bits.reserve(v.size());
|
||||
for (std::vector<std::string>::const_iterator it = v.begin(); it != v.end(); ++it) {
|
||||
if (IfcUtil::valid_binary_string(*it)) {
|
||||
bits.push_back(boost::dynamic_bitset<>(*it));
|
||||
else if (PyUnicode_Check(obj)) {
|
||||
PyObject* ascii = PyUnicode_AsEncodedString(obj, "UTF-8", "strict");
|
||||
if (ascii) {
|
||||
auto s = std::string(PyBytes_AS_STRING(ascii));
|
||||
Py_DECREF(ascii);
|
||||
if (arg_type == IfcUtil::Argument_LOGICAL) {
|
||||
if (s == "UNKNOWN") {
|
||||
self->i()->set_value(i, boost::logic::tribool(boost::logic::indeterminate));
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
} else if (arg_type == IfcUtil::Argument_STRING) {
|
||||
self->i()->set_value<std::string>(i, s);
|
||||
} else if (arg_type == IfcUtil::Argument_ENUMERATION) {
|
||||
const IfcParse::enumeration_type* enum_type = $self->declaration().schema()->declaration_by_name($self->declaration().type())->as_entity()->
|
||||
attribute_by_index(i)->type_of_attribute()->as_named_type()->declared_type()->as_enumeration_type();
|
||||
|
||||
std::vector<std::string>::const_iterator it = std::find(
|
||||
enum_type->enumeration_items().begin(),
|
||||
enum_type->enumeration_items().end(),
|
||||
s);
|
||||
|
||||
if (it == enum_type->enumeration_items().end()) {
|
||||
throw IfcParse::IfcException(s + " does not name a valid item for " + enum_type->name());
|
||||
}
|
||||
|
||||
self->i()->set_value(i, IfcWrite::IfcWriteArgument::EnumerationReference(it - enum_type->enumeration_items().begin(), it->c_str()));
|
||||
} else if (arg_type == IfcUtil::Argument_BINARY) {
|
||||
if (IfcUtil::valid_binary_string(s)) {
|
||||
boost::dynamic_bitset<> bits(s);
|
||||
self->i()->set_value(i, bits);
|
||||
} else {
|
||||
throw IfcParse::IfcException("String not a valid binary representation");
|
||||
}
|
||||
} else {
|
||||
throw IfcParse::IfcException("String not a valid binary representation");
|
||||
}
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(bits);
|
||||
self->data().setArgument(i, arg);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
|
||||
void setArgumentAsEntityInstance(unsigned int i, entity_instance* v) {
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_ENTITY_INSTANCE) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v);
|
||||
self->data().setArgument(i, arg);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
else if (PyFloat_Check(obj)) {
|
||||
double v = PyFloat_AsDouble(obj);
|
||||
if (arg_type == IfcUtil::Argument_DOUBLE) {
|
||||
self->i()->set_value(i, v);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setArgumentAsAggregateOfEntityInstance(unsigned int i, aggregate_of_instance::ptr v) {
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v);
|
||||
self->data().setArgument(i, arg);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
else if (check_aggregate_of_type(obj, get_python_type<int>())) {
|
||||
auto v = python_sequence_as_vector<int>(obj);
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_INT) {
|
||||
self->i()->set_value(i, v);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setArgumentAsAggregateOfAggregateOfInt(unsigned int i, const std::vector< std::vector<int> >& v) {
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_INT) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v);
|
||||
self->data().setArgument(i, arg);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
else if (check_aggregate_of_type(obj, get_python_type<double>())) {
|
||||
auto v = python_sequence_as_vector<double>(obj);
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_DOUBLE) {
|
||||
self->i()->set_value(i, v);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setArgumentAsAggregateOfAggregateOfDouble(unsigned int i, const std::vector< std::vector<double> >& v) {
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v);
|
||||
self->data().setArgument(i, arg);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
else if (check_aggregate_of_type(obj, get_python_type<std::string>())) {
|
||||
auto v = python_sequence_as_vector<std::string>(obj);
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_STRING) {
|
||||
self->i()->set_value(i, v);
|
||||
} else if (arg_type == IfcUtil::Argument_AGGREGATE_OF_BINARY) {
|
||||
std::vector< boost::dynamic_bitset<> > bits;
|
||||
bits.reserve(v.size());
|
||||
for (std::vector<std::string>::const_iterator it = v.begin(); it != v.end(); ++it) {
|
||||
if (IfcUtil::valid_binary_string(*it)) {
|
||||
bits.push_back(boost::dynamic_bitset<>(*it));
|
||||
} else {
|
||||
throw IfcParse::IfcException("String not a valid binary representation");
|
||||
}
|
||||
}
|
||||
self->i()->set_value(i, bits);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setArgumentAsAggregateOfAggregateOfEntityInstance(unsigned int i, aggregate_of_aggregate_of_instance::ptr v) {
|
||||
IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i);
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE) {
|
||||
IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument();
|
||||
arg->set(v);
|
||||
self->data().setArgument(i, arg);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
else if (auto inst = cast_pyobject<entity_instance*>(obj)) {
|
||||
if (arg_type == IfcUtil::Argument_ENTITY_INSTANCE) {
|
||||
self->i()->set_value(i, inst->i());
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
|
||||
else if (check_aggregate_of_aggregate_of_type(obj, get_python_type<int>())) {
|
||||
auto v = python_sequence_as_vector_of_vector<int>(obj);
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_INT) {
|
||||
self->i()->set_value(i, v);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
|
||||
else if (check_aggregate_of_aggregate_of_type(obj, get_python_type<double>())) {
|
||||
auto v = python_sequence_as_vector_of_vector<double>(obj);
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE) {
|
||||
self->i()->set_value(i, v);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
|
||||
else if (PySequence_Check(obj)) {
|
||||
aggregate_of_instance::ptr insts1d(new aggregate_of_instance);
|
||||
aggregate_of_aggregate_of_instance::ptr insts2d(new aggregate_of_aggregate_of_instance);
|
||||
for(Py_ssize_t i = 0; i < PySequence_Size(obj); ++i) {
|
||||
PyObject* element_i = PySequence_GetItem(obj, i);
|
||||
if (auto inst = cast_pyobject<entity_instance*>(element_i); insts1d) {
|
||||
insts2d.reset();
|
||||
|
||||
insts1d->push(*inst);
|
||||
} else if (PySequence_Check(element_i) && insts2d) {
|
||||
insts1d.reset();
|
||||
|
||||
std::vector<IfcUtil::IfcBaseClass*> inner;
|
||||
for(Py_ssize_t j = 0; j < PySequence_Size(element_i); ++j) {
|
||||
PyObject* element_j = PySequence_GetItem(element_i, j);
|
||||
if (auto inst = cast_pyobject<entity_instance*>(element_j); insts2d) {
|
||||
inner.push_back(*inst);
|
||||
} else {
|
||||
insts2d.reset();
|
||||
}
|
||||
Py_DECREF(element_j);
|
||||
if (!insts2d) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (insts2d) {
|
||||
insts2d->push(inner);
|
||||
}
|
||||
} else {
|
||||
insts1d.reset();
|
||||
insts2d.reset();
|
||||
}
|
||||
Py_DECREF(element_i);
|
||||
if (!insts1d && !insts2d) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (arg_type == IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE && insts1d) {
|
||||
self->i()->set_value(i, insts1d);
|
||||
} else if (arg_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE && insts2d) {
|
||||
self->i()->set_value(i, insts2d);
|
||||
} else {
|
||||
throw IfcParse::IfcException("Attribute not set");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
%extend entity_instance {
|
||||
%pythoncode %{
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return custom_base.__getattr__(self, name)
|
||||
except:
|
||||
return _swig_getattr(self, entity_instance, name)
|
||||
def __setattr__(self, name, value):
|
||||
try:
|
||||
return custom_base.__setattr__(self, name, value)
|
||||
except:
|
||||
return _swig_setattr(self, entity_instance, name, value)
|
||||
%}
|
||||
};
|
||||
|
||||
%extend IfcParse::IfcSpfHeader {
|
||||
%pythoncode %{
|
||||
# Hide the getters with read-only property implementations
|
||||
@@ -696,7 +843,7 @@ IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsi
|
||||
return IFCOPENSHELL_VERSION;
|
||||
}
|
||||
|
||||
entity_instance new_IfcBaseClass(const std::string& schema_identifier, const std::string& name) {
|
||||
entity_instance* make_instance(const std::string& schema_identifier, const std::string& name) {
|
||||
const IfcParse::schema_definition* schema = IfcParse::schema_by_name(schema_identifier);
|
||||
const IfcParse::declaration* decl = schema->declaration_by_name(name);
|
||||
IfcEntityInstanceData* data = new IfcEntityInstanceData(decl);
|
||||
@@ -719,7 +866,7 @@ IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsi
|
||||
}
|
||||
}
|
||||
|
||||
return schema->instantiate(data);
|
||||
return new entity_instance(schema->instantiate(data));
|
||||
}
|
||||
%}
|
||||
|
||||
|
||||
@@ -149,12 +149,6 @@
|
||||
// Create docstrings for generated python code.
|
||||
%feature("autodoc", "1");
|
||||
|
||||
%include "utils/type_conversion.i"
|
||||
|
||||
%include "utils/typemaps_in.i"
|
||||
|
||||
%include "utils/typemaps_out.i"
|
||||
|
||||
%module ifcopenshell_wrapper %{
|
||||
#include "../ifcgeom/Converter.h"
|
||||
#include "../ifcgeom/taxonomy.h"
|
||||
|
||||
@@ -79,10 +79,10 @@
|
||||
}
|
||||
|
||||
template <>
|
||||
IfcUtil::IfcBaseClass* cast_pyobject(PyObject* element) {
|
||||
entity_instance* cast_pyobject(PyObject* element) {
|
||||
void *arg = 0;
|
||||
int res = SWIG_ConvertPtr(element, &arg, SWIGTYPE_p_IfcUtil__IfcBaseClass, 0);
|
||||
return static_cast<IfcUtil::IfcBaseClass*>(SWIG_IsOK(res) ? arg : 0);
|
||||
int res = SWIG_ConvertPtr(element, &arg, SWIGTYPE_p_entity_instance, 0);
|
||||
return static_cast<entity_instance*>(SWIG_IsOK(res) ? arg : 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -156,6 +156,7 @@
|
||||
PyObject* pythonize(const double& t) { return PyFloat_FromDouble(t); }
|
||||
PyObject* pythonize(const std::string& t) { return PyUnicode_FromString(t.c_str()); }
|
||||
PyObject* pythonize(const IfcUtil::IfcBaseClass* t) { return SWIG_NewPointerObj(SWIG_as_voidptr(t), SWIGTYPE_p_IfcUtil__IfcBaseClass, 0); }
|
||||
PyObject* pythonize(const entity_instance* t) { return SWIG_NewPointerObj(SWIG_as_voidptr(t), SWIGTYPE_p_entity_instance, SWIG_POINTER_OWN);}
|
||||
PyObject* pythonize(const IfcParse::attribute* t) { return SWIG_NewPointerObj(SWIG_as_voidptr(t), SWIGTYPE_p_IfcParse__attribute, 0); }
|
||||
PyObject* pythonize(const IfcParse::inverse_attribute* t) { return SWIG_NewPointerObj(SWIG_as_voidptr(t), SWIGTYPE_p_IfcParse__inverse_attribute, 0); }
|
||||
PyObject* pythonize(const IfcParse::entity* t) { return SWIG_NewPointerObj(SWIG_as_voidptr(t), SWIGTYPE_p_IfcParse__entity, 0); }
|
||||
|
||||
@@ -23,13 +23,17 @@
|
||||
$result = SWIG_Python_str_FromChar(data_type_strings[(int)$1]);
|
||||
}
|
||||
|
||||
%typemap(out) std::pair<IfcUtil::ArgumentType, Argument*> {
|
||||
%typemap(out) std::tuple<IfcUtil::ArgumentType, Argument*, entity_instance*> {
|
||||
// The SWIG %exception directive does not take care
|
||||
// of our typemap. So the attribute conversion block
|
||||
// is wrapped in a try-catch block manually.
|
||||
try {
|
||||
const Argument& arg = *($1.second);
|
||||
const IfcUtil::ArgumentType type = $1.first;
|
||||
|
||||
std::tuple<IfcUtil::ArgumentType, Argument*, entity_instance*>& res = $1;
|
||||
const Argument& arg = *std::get<Argument*>(res);
|
||||
const IfcUtil::ArgumentType type = std::get<IfcUtil::ArgumentType>(res);
|
||||
entity_instance* host = std::get<entity_instance*>(res);
|
||||
|
||||
if (arg.isNull()) {
|
||||
Py_INCREF(Py_None);
|
||||
$result = Py_None;
|
||||
@@ -81,7 +85,10 @@
|
||||
break; }
|
||||
case IfcUtil::Argument_ENTITY_INSTANCE: {
|
||||
IfcUtil::IfcBaseClass* v = arg;
|
||||
$result = pythonize(v);
|
||||
if (!v->data().file) {
|
||||
throw std::runtime_error("No file");
|
||||
}
|
||||
$result = pythonize(new entity_instance(v->data().file->instance_by_id_2(v->data().id()), host->file_pointer()));
|
||||
break; }
|
||||
case IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE: {
|
||||
aggregate_of_instance::ptr v = arg;
|
||||
|
||||
Reference in New Issue
Block a user