mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 10:11:46 +00:00
Clean branch
This commit is contained in:
@@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# This can be packaged with `pyinstaller --onefile --clean --icon=icon.ico ifcpatch.py`
|
||||||
|
|
||||||
|
import ifcopenshell
|
||||||
|
import logging
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
def execute(args, is_library=None):
|
||||||
|
logging.basicConfig(filename=args['log'], filemode='a', level=logging.DEBUG)
|
||||||
|
logger = logging.getLogger('IFCPatch')
|
||||||
|
print('# Loading IFC file ...')
|
||||||
|
ifc_file = ifcopenshell.open(args['input'])
|
||||||
|
print('# Loading patch recipe ...')
|
||||||
|
patcher = getattr(__import__('recipes.{}'.format(args['recipe'])), args['recipe']).Patcher(
|
||||||
|
args['input'], ifc_file, logger, args['arguments'])
|
||||||
|
print('# Patching ...')
|
||||||
|
patcher.patch()
|
||||||
|
ifc_file = patcher.file
|
||||||
|
if is_library is True:
|
||||||
|
return ifc_file
|
||||||
|
print('# Writing patched file ...')
|
||||||
|
if not args['output']:
|
||||||
|
args['output'] = args['input']
|
||||||
|
if isinstance(ifc_file, str):
|
||||||
|
with open(args['output'], 'w') as text_file:
|
||||||
|
text_file.write(ifc_file)
|
||||||
|
else:
|
||||||
|
ifc_file.write(args['output'])
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Patches IFC files to fix badly formatted data')
|
||||||
|
parser.add_argument(
|
||||||
|
'-i',
|
||||||
|
'--input',
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
help='The IFC file to patch')
|
||||||
|
parser.add_argument(
|
||||||
|
'-o',
|
||||||
|
'--output',
|
||||||
|
type=str,
|
||||||
|
help='The output file to save the patched IFC')
|
||||||
|
parser.add_argument(
|
||||||
|
'-r',
|
||||||
|
'--recipe',
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
help='Name of the recipe to use when patching')
|
||||||
|
parser.add_argument(
|
||||||
|
'-l',
|
||||||
|
'--log',
|
||||||
|
type=str,
|
||||||
|
help='Specify a log file',
|
||||||
|
default='ifcpatch.log')
|
||||||
|
parser.add_argument(
|
||||||
|
'-a',
|
||||||
|
'--arguments',
|
||||||
|
nargs='+',
|
||||||
|
help='Specify custom arguments to the patch recipe')
|
||||||
|
args = vars(parser.parse_args())
|
||||||
|
|
||||||
|
execute(args)
|
||||||
|
|
||||||
|
print('# All tasks are complete :-)')
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.selector
|
||||||
|
|
||||||
|
|
||||||
|
class Patcher:
|
||||||
|
def __init__(self, src, file, logger, args=None):
|
||||||
|
self.src = src
|
||||||
|
self.file = file
|
||||||
|
self.logger = logger
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
self.contained_ins = {}
|
||||||
|
self.aggregates = {}
|
||||||
|
self.new = ifcopenshell.file(schema=self.file.wrapped_data.schema)
|
||||||
|
self.owner_history = None
|
||||||
|
for owner_history in self.file.by_type("IfcOwnerHistory"):
|
||||||
|
self.owner_history = self.new.add(owner_history)
|
||||||
|
break
|
||||||
|
selector = ifcopenshell.util.selector.Selector()
|
||||||
|
for element in selector.parse(self.file, self.args[0]):
|
||||||
|
self.add_element(element)
|
||||||
|
self.create_spatial_tree()
|
||||||
|
self.file = self.new
|
||||||
|
|
||||||
|
def add_element(self, element):
|
||||||
|
new_element = self.new.add(element)
|
||||||
|
for rel in element.ContainedInStructure:
|
||||||
|
spatial_element = rel.RelatingStructure
|
||||||
|
new_spatial_element = self.new.add(spatial_element)
|
||||||
|
self.contained_ins.setdefault(spatial_element.GlobalId, set()).add(new_element)
|
||||||
|
self.add_spatial_tree(spatial_element, new_spatial_element)
|
||||||
|
|
||||||
|
def add_spatial_tree(self, spatial_element, new_spatial_element):
|
||||||
|
for rel in spatial_element.Decomposes:
|
||||||
|
new = self.new.add(rel.RelatingObject)
|
||||||
|
self.aggregates.setdefault(rel.RelatingObject.GlobalId, set()).add(new_spatial_element)
|
||||||
|
self.add_spatial_tree(rel.RelatingObject, new)
|
||||||
|
|
||||||
|
def create_spatial_tree(self):
|
||||||
|
for relating_structure, related_elements in self.contained_ins.items():
|
||||||
|
self.new.createIfcRelContainedInSpatialStructure(
|
||||||
|
ifcopenshell.guid.new(),
|
||||||
|
self.owner_history,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
list(related_elements),
|
||||||
|
self.new.by_guid(relating_structure),
|
||||||
|
)
|
||||||
|
for relating_object, related_objects in self.aggregates.items():
|
||||||
|
self.new.createIfcRelAggregates(
|
||||||
|
ifcopenshell.guid.new(),
|
||||||
|
self.owner_history,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
self.new.by_guid(relating_object),
|
||||||
|
list(related_objects),
|
||||||
|
)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
class Patcher:
|
||||||
|
def __init__(self, src, file, logger, args=None):
|
||||||
|
self.src = src
|
||||||
|
self.file = file
|
||||||
|
self.logger = logger
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
source = ifcopenshell.open(self.args[0])
|
||||||
|
original_project = self.file.by_type('IfcProject')[0]
|
||||||
|
merged_project = self.file.add(source.by_type('IfcProject')[0])
|
||||||
|
for element in source.by_type('IfcRoot'):
|
||||||
|
self.file.add(element)
|
||||||
|
for inverse in self.file.get_inverse(merged_project):
|
||||||
|
ifcopenshell.util.element.replace_attribute(inverse, merged_project, original_project)
|
||||||
|
self.file.remove(merged_project)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.schema
|
||||||
|
|
||||||
|
|
||||||
|
class Patcher:
|
||||||
|
def __init__(self, src, file, logger, args=None):
|
||||||
|
self.src = src
|
||||||
|
self.file = file
|
||||||
|
self.logger = logger
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
self.new = ifcopenshell.file(schema=self.args[0])
|
||||||
|
migrator = ifcopenshell.util.schema.Migrator()
|
||||||
|
for element in self.file:
|
||||||
|
print("Migrating", element)
|
||||||
|
print("Successfully converted to", migrator.migrate(element, self.new))
|
||||||
|
self.file = self.new
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
class Patcher:
|
||||||
|
def __init__(self, src, file, logger, args=None):
|
||||||
|
self.src = src
|
||||||
|
self.file = file
|
||||||
|
self.logger = logger
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
absolute_placements = []
|
||||||
|
|
||||||
|
for product in self.file.by_type('IfcProduct'):
|
||||||
|
if not product.ObjectPlacement:
|
||||||
|
continue
|
||||||
|
absolute_placement = self.get_absolute_placement(product.ObjectPlacement)
|
||||||
|
if absolute_placement.is_a('IfcLocalPlacement'):
|
||||||
|
absolute_placements.append(absolute_placement)
|
||||||
|
absolute_placements = set(absolute_placements)
|
||||||
|
|
||||||
|
for placement in absolute_placements:
|
||||||
|
offset_location = (
|
||||||
|
placement.RelativePlacement.Location.Coordinates[0] + float(self.args[0]),
|
||||||
|
placement.RelativePlacement.Location.Coordinates[1] + float(self.args[1]),
|
||||||
|
placement.RelativePlacement.Location.Coordinates[2] + float(self.args[2])
|
||||||
|
)
|
||||||
|
|
||||||
|
relative_placement = self.file.createIfcAxis2Placement3D(
|
||||||
|
self.file.createIfcCartesianPoint(offset_location))
|
||||||
|
|
||||||
|
if placement.RelativePlacement.Axis:
|
||||||
|
relative_placement.Axis = placement.RelativePlacement.Axis
|
||||||
|
if placement.RelativePlacement.RefDirection:
|
||||||
|
relative_placement.RefDirection = placement.RelativePlacement.RefDirection
|
||||||
|
|
||||||
|
angle = float(self.args[3])
|
||||||
|
if not angle:
|
||||||
|
placement.RelativePlacement = relative_placement
|
||||||
|
continue
|
||||||
|
|
||||||
|
rotation_matrix = self.z_rotation_matrix(math.radians(angle))
|
||||||
|
relative_placement.Location.Coordinates = self.multiply_by_matrix(offset_location, rotation_matrix)
|
||||||
|
|
||||||
|
if placement.RelativePlacement.Axis:
|
||||||
|
z_axis = placement.RelativePlacement.Axis.DirectionRatios
|
||||||
|
relative_placement.Axis = self.file.createIfcDirection(
|
||||||
|
self.multiply_by_matrix(z_axis, rotation_matrix))
|
||||||
|
|
||||||
|
if placement.RelativePlacement.RefDirection:
|
||||||
|
x_axis = placement.RelativePlacement.RefDirection.DirectionRatios
|
||||||
|
else:
|
||||||
|
x_axis = (1., 0., 0.)
|
||||||
|
relative_placement.RefDirection = self.file.createIfcDirection(
|
||||||
|
self.multiply_by_matrix(x_axis, rotation_matrix))
|
||||||
|
|
||||||
|
placement.RelativePlacement = relative_placement
|
||||||
|
|
||||||
|
def get_absolute_placement(self, object_placement):
|
||||||
|
if object_placement.PlacementRelTo:
|
||||||
|
return self.get_absolute_placement(object_placement.PlacementRelTo)
|
||||||
|
return object_placement
|
||||||
|
|
||||||
|
def z_rotation_matrix(self, angle):
|
||||||
|
return [
|
||||||
|
[math.cos(angle), -math.sin(angle), 0.],
|
||||||
|
[math.sin(angle), math.cos(angle), 0.],
|
||||||
|
[0., 0., 1.]
|
||||||
|
]
|
||||||
|
|
||||||
|
def multiply_by_matrix(self, v, m):
|
||||||
|
return [
|
||||||
|
v[0]*m[0][0] + v[1]*m[0][1] + v[2]*m[0][2],
|
||||||
|
v[0]*m[1][0] + v[1]*m[1][1] + v[2]*m[1][2],
|
||||||
|
v[0]*m[2][0] + v[1]*m[2][1] + v[2]*m[2][2]
|
||||||
|
]
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
class Patcher:
|
||||||
|
def __init__(self, src, file, logger, args=None):
|
||||||
|
self.src = src
|
||||||
|
self.file = file
|
||||||
|
self.logger = logger
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
project = self.file.by_type('IfcProject')[0]
|
||||||
|
storeys = self.find_decomposed_ifc_class(project, 'IfcBuildingStorey')
|
||||||
|
for storey in storeys:
|
||||||
|
co = storey.ObjectPlacement.RelativePlacement.Location.Coordinates
|
||||||
|
storey.ObjectPlacement.RelativePlacement.Location.Coordinates = (co[0], co[1], co[2]+float(self.args[0]))
|
||||||
|
co = storey.ObjectPlacement.RelativePlacement.Location.Coordinates
|
||||||
|
# NOTE If the geometric data is provided (ObjectPlacement is
|
||||||
|
# specified), the Elevation value shall either not be included, or
|
||||||
|
# be equal to the local placement Z value.
|
||||||
|
storey.Elevation = co[2]
|
||||||
|
|
||||||
|
def find_decomposed_ifc_class(self, element, ifc_class):
|
||||||
|
results = []
|
||||||
|
rel_aggregates = element.IsDecomposedBy
|
||||||
|
if not rel_aggregates:
|
||||||
|
return results
|
||||||
|
for rel_aggregate in rel_aggregates:
|
||||||
|
for part in rel_aggregate.RelatedObjects:
|
||||||
|
if part.is_a(ifc_class):
|
||||||
|
results.append(part)
|
||||||
|
results.extend(self.find_decomposed_ifc_class(part, ifc_class))
|
||||||
|
return results
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
from toposort import toposort_flatten as toposort
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Patcher:
|
||||||
|
def __init__(self, src, file, logger, args=None):
|
||||||
|
self.src = src
|
||||||
|
self.file = file
|
||||||
|
self.logger = logger
|
||||||
|
self.args = args
|
||||||
|
self.optimized_file = ifcopenshell.file(schema=self.file.schema)
|
||||||
|
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
def generate_instances_and_references():
|
||||||
|
"""
|
||||||
|
Generator which yields an entity id and
|
||||||
|
the set of all of its references contained in its attributes.
|
||||||
|
"""
|
||||||
|
for inst in self.file:
|
||||||
|
yield inst.id(), set(i.id() for i in self.file.traverse(inst)[1:] if i.id())
|
||||||
|
|
||||||
|
instance_mapping = {}
|
||||||
|
|
||||||
|
|
||||||
|
def map_value(v):
|
||||||
|
"""
|
||||||
|
Recursive function which replicates an entity instance, with
|
||||||
|
its attributes, mapping references to already registered
|
||||||
|
instances. Indeed, because of the toposort we know that
|
||||||
|
forward attribute value instances are mapped before the instances
|
||||||
|
that reference them.
|
||||||
|
"""
|
||||||
|
if isinstance(v, (list, tuple)):
|
||||||
|
# lists are recursively traversed
|
||||||
|
return type(v)(map(map_value, v))
|
||||||
|
elif isinstance(v, ifcopenshell.entity_instance):
|
||||||
|
if v.id() == 0:
|
||||||
|
# express simple types are not part of the toposort and just copied
|
||||||
|
return self.optimized_file.create_entity(v.is_a(), v[0])
|
||||||
|
|
||||||
|
|
||||||
|
return instance_mapping[v]
|
||||||
|
else:
|
||||||
|
# a plain python value can just be returned
|
||||||
|
return v
|
||||||
|
|
||||||
|
info_to_id = {}
|
||||||
|
|
||||||
|
for id in toposort(dict(generate_instances_and_references())):
|
||||||
|
inst = self.file[id]
|
||||||
|
info = inst.get_info(include_identifier=False, recursive=True, return_type=frozenset)
|
||||||
|
if info in info_to_id:
|
||||||
|
mapped = instance_mapping[inst] = instance_mapping[self.file[info_to_id[info]]]
|
||||||
|
|
||||||
|
else:
|
||||||
|
info_to_id[info] = id
|
||||||
|
instance_mapping[inst] = self.optimized_file.create_entity(
|
||||||
|
inst.is_a(),
|
||||||
|
*map(map_value, inst)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.file = self.optimized_file
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
from collections import deque
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
class Patcher:
|
||||||
|
def __init__(self, src, file, logger, args=None):
|
||||||
|
self.src = src
|
||||||
|
self.file = file
|
||||||
|
self.logger = logger
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
deleted = []
|
||||||
|
hashes = {}
|
||||||
|
for element in self.file:
|
||||||
|
if element.is_a('IfcRoot'):
|
||||||
|
continue
|
||||||
|
h = hash(tuple(element))
|
||||||
|
if h in hashes:
|
||||||
|
for inverse in self.file.get_inverse(element):
|
||||||
|
ifcopenshell.util.element.replace_attribute(inverse, element, hashes[h])
|
||||||
|
deleted.append(element.id())
|
||||||
|
else:
|
||||||
|
hashes[h] = element
|
||||||
|
deleted.sort()
|
||||||
|
deleted_q = deque(deleted)
|
||||||
|
new = ''
|
||||||
|
for line in self.file.wrapped_data.to_string().split('\n'):
|
||||||
|
try:
|
||||||
|
if int(line.split('=')[0][1:]) != deleted_q[0]:
|
||||||
|
new += (line + '\n')
|
||||||
|
else:
|
||||||
|
deleted_q.popleft()
|
||||||
|
except:
|
||||||
|
new += (line + '\n')
|
||||||
|
self.file = new
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
class Patcher:
|
||||||
|
def __init__(self, src, file, logger, args=None):
|
||||||
|
self.src = src
|
||||||
|
self.file = file
|
||||||
|
self.logger = logger
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
project = self.file.by_type('IfcProject')[0]
|
||||||
|
sites = self.find_decomposed_ifc_class(project, 'IfcSite')
|
||||||
|
for site in sites:
|
||||||
|
site.Representation = None
|
||||||
|
|
||||||
|
def find_decomposed_ifc_class(self, element, ifc_class):
|
||||||
|
results = []
|
||||||
|
rel_aggregates = element.IsDecomposedBy
|
||||||
|
if not rel_aggregates:
|
||||||
|
return results
|
||||||
|
for rel_aggregate in rel_aggregates:
|
||||||
|
for part in rel_aggregate.RelatedObjects:
|
||||||
|
if part.is_a(ifc_class):
|
||||||
|
results.append(part)
|
||||||
|
results.extend(self.find_decomposed_ifc_class(part, ifc_class))
|
||||||
|
return results
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
class Patcher:
|
||||||
|
def __init__(self, src, file, logger, args=None):
|
||||||
|
self.src = src
|
||||||
|
self.file = file
|
||||||
|
self.logger = logger
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
placement_coord_ids = set()
|
||||||
|
for placement in self.file.by_type('IfcObjectPlacement'):
|
||||||
|
[placement_coord_ids.add(e.id()) for e in self.file.traverse(placement) if e.is_a('IfcCartesianPoint')]
|
||||||
|
|
||||||
|
# Arbitrary threshold based on experience
|
||||||
|
self.threshold = 1000000
|
||||||
|
if self.args and len(self.args) == 1:
|
||||||
|
self.threshold = float(self.args[0])
|
||||||
|
elif self.args and len(self.args) == 4:
|
||||||
|
self.threshold = float(self.args[3])
|
||||||
|
|
||||||
|
# This method will not work all the time, but will catch most issues. It
|
||||||
|
# assumes that absolute coordinates are easily recognisable based on
|
||||||
|
# having a large absolute value above a threshold. This is not always
|
||||||
|
# the case, but is very fast to run, and works for most cases.
|
||||||
|
offset_point = None
|
||||||
|
if self.args and len(self.args) >= 3:
|
||||||
|
offset_point = (float(self.args[0]),float(self.args[1]),float(self.args[2]))
|
||||||
|
try:
|
||||||
|
point_lists = self.file.by_type('IfcCartesianPointList3D')
|
||||||
|
except:
|
||||||
|
# IFC2X3 does not have IfcCartesianPointList3D
|
||||||
|
point_lists = []
|
||||||
|
for point_list in point_lists:
|
||||||
|
coord_list = [None] * len(point_list.CoordList)
|
||||||
|
for i, point in enumerate(point_list.CoordList):
|
||||||
|
if len(point) == 2 or not self.is_point_far_away(point):
|
||||||
|
coord_list[i] = point
|
||||||
|
continue
|
||||||
|
if not offset_point:
|
||||||
|
offset_point = (-point[0], -point[1], -point[2])
|
||||||
|
self.logger.info(f'Resetting absolute coordinates by {point}')
|
||||||
|
point = (
|
||||||
|
point[0] + offset_point[0],
|
||||||
|
point[1] + offset_point[1],
|
||||||
|
point[2] + offset_point[2]
|
||||||
|
)
|
||||||
|
coord_list[i] = point
|
||||||
|
point_list.CoordList = coord_list
|
||||||
|
for point in self.file.by_type('IfcCartesianPoint'):
|
||||||
|
if len(point.Coordinates) == 2 or not self.is_point_far_away(point):
|
||||||
|
continue
|
||||||
|
if point.id() in placement_coord_ids:
|
||||||
|
continue
|
||||||
|
if not offset_point:
|
||||||
|
offset_point = (-point.Coordinates[0], -point.Coordinates[1], -point.Coordinates[2])
|
||||||
|
self.logger.info(f'Resetting absolute coordinates by {point}')
|
||||||
|
point.Coordinates = (
|
||||||
|
point.Coordinates[0] + offset_point[0],
|
||||||
|
point.Coordinates[1] + offset_point[1],
|
||||||
|
point.Coordinates[2] + offset_point[2]
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_point_far_away(self, point):
|
||||||
|
if hasattr(point, 'Coordinates'):
|
||||||
|
return abs(point.Coordinates[0]) > self.threshold \
|
||||||
|
or abs(point.Coordinates[1]) > self.threshold \
|
||||||
|
or abs(point.Coordinates[2]) > self.threshold
|
||||||
|
return abs(point[0]) > self.threshold \
|
||||||
|
or abs(point[1]) > self.threshold \
|
||||||
|
or abs(point[2]) > self.threshold
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
class Patcher:
|
||||||
|
def __init__(self, src, file, logger, args=None):
|
||||||
|
self.src = src
|
||||||
|
self.file = file
|
||||||
|
self.logger = logger
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
project = self.file.by_type('IfcProject')[0]
|
||||||
|
spatial_elements = self.find_decomposed_ifc_class(project, self.args[0])
|
||||||
|
for spatial_element in spatial_elements:
|
||||||
|
self.patch_placement_to_origin(spatial_element)
|
||||||
|
|
||||||
|
def find_decomposed_ifc_class(self, element, ifc_class):
|
||||||
|
results = []
|
||||||
|
rel_aggregates = element.IsDecomposedBy
|
||||||
|
if not rel_aggregates:
|
||||||
|
return results
|
||||||
|
for rel_aggregate in rel_aggregates:
|
||||||
|
for part in rel_aggregate.RelatedObjects:
|
||||||
|
if part.is_a(ifc_class):
|
||||||
|
results.append(part)
|
||||||
|
results.extend(self.find_decomposed_ifc_class(part, ifc_class))
|
||||||
|
return results
|
||||||
|
|
||||||
|
def patch_placement_to_origin(self, element):
|
||||||
|
element.ObjectPlacement.RelativePlacement.Location.Coordinates = (0., 0., 0.)
|
||||||
|
if element.ObjectPlacement.RelativePlacement.Axis:
|
||||||
|
element.ObjectPlacement.RelativePlacement.Axis.DirectionRatios = (0., 0., 1.)
|
||||||
|
if element.ObjectPlacement.RelativePlacement.RefDirection:
|
||||||
|
element.ObjectPlacement.RelativePlacement.RefDirection.DirectionRatios = (1., 0., 0.)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
class Patcher:
|
||||||
|
def __init__(self, src, file, logger, args=None):
|
||||||
|
self.src = src
|
||||||
|
self.file = file
|
||||||
|
self.logger = logger
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
project = self.file.by_type('IfcProject')[0]
|
||||||
|
sites = self.find_decomposed_ifc_class(project, 'IfcSite')
|
||||||
|
for site in sites:
|
||||||
|
site.RefElevation = float(self.args[0])
|
||||||
|
|
||||||
|
def find_decomposed_ifc_class(self, element, ifc_class):
|
||||||
|
results = []
|
||||||
|
rel_aggregates = element.IsDecomposedBy
|
||||||
|
if not rel_aggregates:
|
||||||
|
return results
|
||||||
|
for rel_aggregate in rel_aggregates:
|
||||||
|
for part in rel_aggregate.RelatedObjects:
|
||||||
|
if part.is_a(ifc_class):
|
||||||
|
results.append(part)
|
||||||
|
results.extend(self.find_decomposed_ifc_class(part, ifc_class))
|
||||||
|
return results
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
class Patcher:
|
||||||
|
def __init__(self, src, file, logger, args=None):
|
||||||
|
self.src = src
|
||||||
|
self.file = file
|
||||||
|
self.logger = logger
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
import ifcopenshell
|
||||||
|
from shutil import copyfile
|
||||||
|
storeys = self.file.by_type('IfcBuildingStorey')
|
||||||
|
for i, storey in enumerate(storeys):
|
||||||
|
dest = '{}-{}.ifc'.format(i, storey.Name)
|
||||||
|
copyfile(self.src, dest)
|
||||||
|
old_ifc = ifcopenshell.open(dest)
|
||||||
|
new_ifc = ifcopenshell.file(schema=self.file.schema)
|
||||||
|
if self.file.schema == 'IFC2X3':
|
||||||
|
elements = old_ifc.by_type('IfcProject') + old_ifc.by_type('IfcProduct')
|
||||||
|
else:
|
||||||
|
elements = old_ifc.by_type('IfcContext') + old_ifc.by_type('IfcProduct')
|
||||||
|
inverse_elements = []
|
||||||
|
for element in elements:
|
||||||
|
if element.is_a('IfcElement') \
|
||||||
|
and not self.is_in_storey(element, storey):
|
||||||
|
element.Representation = None
|
||||||
|
continue
|
||||||
|
if element.is_a('IfcElement'):
|
||||||
|
styled_rep_items = [i for i in old_ifc.traverse(element) if i.is_a('IfcRepresentationItem') and i.StyledByItem]
|
||||||
|
[new_ifc.add(i.StyledByItem[0]) for i in styled_rep_items]
|
||||||
|
new_ifc.add(element)
|
||||||
|
inverse_elements.extend(old_ifc.get_inverse(element))
|
||||||
|
for inverse_element in inverse_elements:
|
||||||
|
new_ifc.add(inverse_element)
|
||||||
|
for element in new_ifc.by_type('IfcElement'):
|
||||||
|
if not self.is_in_storey(element, storey):
|
||||||
|
new_ifc.remove(element)
|
||||||
|
new_ifc.write(dest)
|
||||||
|
|
||||||
|
def is_in_storey(self, element, storey):
|
||||||
|
return element.ContainedInStructure \
|
||||||
|
and element.ContainedInStructure[0].RelatingStructure.is_a('IfcBuildingStorey') \
|
||||||
|
and element.ContainedInStructure[0].RelatingStructure.GlobalId == storey.GlobalId
|
||||||
Reference in New Issue
Block a user