mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 11:53:44 +00:00
Update export_ifc.py
Replace object by obj - never override native var "object" Implements get_axis from matrix helper
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import csv
|
import csv
|
||||||
import json
|
import json
|
||||||
@@ -7,10 +9,12 @@ from mathutils import Vector, Matrix
|
|||||||
from .helper import SIUnitHelper
|
from .helper import SIUnitHelper
|
||||||
from . import ifcopenshell
|
from . import ifcopenshell
|
||||||
|
|
||||||
|
|
||||||
class ArrayModifier:
|
class ArrayModifier:
|
||||||
count: int
|
count: int
|
||||||
offset: Vector
|
offset: Vector
|
||||||
|
|
||||||
|
|
||||||
class QtoCalculator():
|
class QtoCalculator():
|
||||||
def get_units(self, o, vg_index):
|
def get_units(self, o, vg_index):
|
||||||
return len([v for v in o.data.vertices if vg_index in [g.group for g in v.groups]])
|
return len([v for v in o.data.vertices if vg_index in [g.group for g in v.groups]])
|
||||||
@@ -25,8 +29,8 @@ class QtoCalculator():
|
|||||||
length += self.get_edge_distance(o, e)
|
length += self.get_edge_distance(o, e)
|
||||||
return length
|
return length
|
||||||
|
|
||||||
def get_edge_distance(self, object, edge):
|
def get_edge_distance(self, obj, edge):
|
||||||
return (object.data.vertices[edge.vertices[1]].co - object.data.vertices[edge.vertices[0]].co).length
|
return (obj.data.vertices[edge.vertices[1]].co - obj.data.vertices[edge.vertices[0]].co).length
|
||||||
|
|
||||||
def get_area(self, o, vg_index):
|
def get_area(self, o, vg_index):
|
||||||
area = 0
|
area = 0
|
||||||
@@ -63,6 +67,7 @@ class QtoCalculator():
|
|||||||
volume += v1.dot(v2.cross(v3)) / 6.0
|
volume += v1.dot(v2.cross(v3)) / 6.0
|
||||||
return volume
|
return volume
|
||||||
|
|
||||||
|
|
||||||
class IfcSchema():
|
class IfcSchema():
|
||||||
def __init__(self, ifc_export_settings):
|
def __init__(self, ifc_export_settings):
|
||||||
self.schema_dir = ifc_export_settings.schema_dir
|
self.schema_dir = ifc_export_settings.schema_dir
|
||||||
@@ -86,6 +91,7 @@ class IfcSchema():
|
|||||||
self.psets[property.Name] = {
|
self.psets[property.Name] = {
|
||||||
'HasPropertyTemplates': {p.Name: p for p in property.HasPropertyTemplates}}
|
'HasPropertyTemplates': {p.Name: p for p in property.HasPropertyTemplates}}
|
||||||
|
|
||||||
|
|
||||||
class IfcParser():
|
class IfcParser():
|
||||||
def __init__(self, ifc_export_settings):
|
def __init__(self, ifc_export_settings):
|
||||||
self.data_dir = ifc_export_settings.data_dir
|
self.data_dir = ifc_export_settings.data_dir
|
||||||
@@ -201,32 +207,34 @@ class IfcParser():
|
|||||||
return scale
|
return scale
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def get_object_attributes(self, object):
|
def get_object_attributes(self, obj):
|
||||||
attributes = { 'Name': self.get_ifc_name(object.name) }
|
attributes = {'Name': self.get_ifc_name(obj.name)}
|
||||||
if object.BIMObjectProperties.attributes.find('GlobalId') == -1:
|
if obj.BIMObjectProperties.attributes.find('GlobalId') == -1:
|
||||||
global_id = object.BIMObjectProperties.attributes.add()
|
global_id = obj.BIMObjectProperties.attributes.add()
|
||||||
global_id.name = 'GlobalId'
|
global_id.name = 'GlobalId'
|
||||||
global_id.string_value = ifcopenshell.guid.new()
|
global_id.string_value = ifcopenshell.guid.new()
|
||||||
attributes.update({ a.name: a.string_value for a in object.BIMObjectProperties.attributes})
|
attributes.update({a.name: a.string_value for a in obj.BIMObjectProperties.attributes})
|
||||||
return attributes
|
return attributes
|
||||||
|
|
||||||
def get_products(self):
|
def get_products(self):
|
||||||
for product in self.selected_products:
|
for product in self.selected_products:
|
||||||
object = product['raw']
|
obj = product['raw']
|
||||||
self.add_product(self.get_product(product))
|
self.add_product(self.get_product(product))
|
||||||
self.resolve_array_modifier(product)
|
self.resolve_array_modifier(product)
|
||||||
|
|
||||||
def resolve_array_modifier(self, product):
|
def resolve_array_modifier(self, product):
|
||||||
object = product['raw']
|
obj = product['raw']
|
||||||
instance_objects = [(object, object.matrix_world.translation)]
|
instance_objects = [(obj, obj.matrix_world.translation)]
|
||||||
global_id_index = 0
|
global_id_index = 0
|
||||||
for instance in self.get_instances(object):
|
for instance in self.get_instances(obj):
|
||||||
created_instances = []
|
created_instances = []
|
||||||
for n in range(instance.count - 1):
|
for n in range(instance.count - 1):
|
||||||
for o in instance_objects:
|
for o in instance_objects:
|
||||||
location = o[1] + ((n + 1) * instance.offset)
|
location = o[1] + ((n + 1) * instance.offset)
|
||||||
self.add_product(self.get_product({'raw': o[0], 'metadata': product['metadata']},
|
self.add_product(self.get_product({'raw': o[0], 'metadata': product['metadata']},
|
||||||
{'location': location}, {'GlobalId': self.get_parametric_global_id(object, global_id_index)}))
|
{'location': location}, {
|
||||||
|
'GlobalId': self.get_parametric_global_id(obj,
|
||||||
|
global_id_index)}))
|
||||||
created_instances.append((o[0], location))
|
created_instances.append((o[0], location))
|
||||||
instance_objects.extend(created_instances)
|
instance_objects.extend(created_instances)
|
||||||
|
|
||||||
@@ -247,12 +255,15 @@ class IfcParser():
|
|||||||
self.rel_fills_elements[void['ifc']] = []
|
self.rel_fills_elements[void['ifc']] = []
|
||||||
self.rel_fills_elements[void['ifc']].append(fill['ifc'])
|
self.rel_fills_elements[void['ifc']].append(fill['ifc'])
|
||||||
|
|
||||||
def get_parametric_global_id(self, object, index):
|
def get_axis(self, matrix, axis):
|
||||||
global_ids = object.BIMObjectProperties.global_ids
|
return matrix.row[axis].to_3d().normalized()
|
||||||
|
|
||||||
|
def get_parametric_global_id(self, obj, index):
|
||||||
|
global_ids = obj.BIMObjectProperties.global_ids
|
||||||
total_global_ids = len(global_ids)
|
total_global_ids = len(global_ids)
|
||||||
if index < total_global_ids:
|
if index < total_global_ids:
|
||||||
return global_ids[index].name
|
return global_ids[index].name
|
||||||
global_id = object.BIMObjectProperties.global_ids.add()
|
global_id = obj.BIMObjectProperties.global_ids.add()
|
||||||
global_id.name = ifcopenshell.guid.new()
|
global_id.name = ifcopenshell.guid.new()
|
||||||
return global_id.name
|
return global_id.name
|
||||||
|
|
||||||
@@ -272,38 +283,41 @@ class IfcParser():
|
|||||||
return product
|
return product
|
||||||
|
|
||||||
def get_product(self, selected_product, metadata_override={}, attribute_override={}):
|
def get_product(self, selected_product, metadata_override={}, attribute_override={}):
|
||||||
object = selected_product['raw']
|
obj = selected_product['raw']
|
||||||
product = {
|
product = {
|
||||||
'ifc': None,
|
'ifc': None,
|
||||||
'raw': object,
|
'raw': object,
|
||||||
'location': object.matrix_world.translation,
|
'location': object.matrix_world.translation,
|
||||||
'up_axis': object.matrix_world.to_quaternion() @ Vector((0, 0, 1)),
|
'up_axis': self.get_axis(obj.matrix_world, 2),
|
||||||
'forward_axis': object.matrix_world.to_quaternion() @ Vector((1, 0, 0)),
|
'forward_axis': self.get_axis(obj.matrix_world, 0),
|
||||||
'class': self.get_ifc_class(object.name),
|
'right_axis': self.get_axis(obj.matrix_world, 1),
|
||||||
|
'has_scale': obj.scale != Vector((1, 1, 1)),
|
||||||
|
'scale': obj.scale,
|
||||||
|
'class': self.get_ifc_class(obj.name),
|
||||||
'relating_structure': None,
|
'relating_structure': None,
|
||||||
'relating_host': None,
|
'relating_host': None,
|
||||||
'relating_qtos_key': None,
|
'relating_qtos_key': None,
|
||||||
'representations': self.get_object_representation_names(object),
|
'representations': self.get_object_representation_names(obj),
|
||||||
'attributes': self.get_object_attributes(object)
|
'attributes': self.get_object_attributes(obj)
|
||||||
}
|
}
|
||||||
product['attributes'].update(attribute_override)
|
product['attributes'].update(attribute_override)
|
||||||
product.update(metadata_override)
|
product.update(metadata_override)
|
||||||
|
|
||||||
if object.parent \
|
if obj.parent \
|
||||||
and self.is_a_type(self.get_ifc_class(object.parent.name)):
|
and self.is_a_type(self.get_ifc_class(obj.parent.name)):
|
||||||
reference = self.get_type_product_reference(object.parent.name)
|
reference = self.get_type_product_reference(obj.parent.name)
|
||||||
self.rel_defines_by_type.setdefault(reference, []).append(self.product_index)
|
self.rel_defines_by_type.setdefault(reference, []).append(self.product_index)
|
||||||
|
|
||||||
for collection in product['raw'].users_collection:
|
for collection in product['raw'].users_collection:
|
||||||
self.parse_product_collection(product, collection)
|
self.parse_product_collection(product, collection)
|
||||||
|
|
||||||
if 'IfcRelNests' in object.constraints:
|
if 'IfcRelNests' in obj.constraints:
|
||||||
parent_product_index = self.get_product_index_from_raw_name(
|
parent_product_index = self.get_product_index_from_raw_name(
|
||||||
object.constraints['IfcRelNests'].target.name)
|
obj.constraints['IfcRelNests'].target.name)
|
||||||
self.rel_nests.setdefault(parent_product_index, []).append(product)
|
self.rel_nests.setdefault(parent_product_index, []).append(product)
|
||||||
product['relating_host'] = parent_product_index
|
product['relating_host'] = parent_product_index
|
||||||
|
|
||||||
for name, constraint in object.constraints.items():
|
for name, constraint in obj.constraints.items():
|
||||||
if 'IfcRelSpaceBoundary' not in name:
|
if 'IfcRelSpaceBoundary' not in name:
|
||||||
continue
|
continue
|
||||||
self.rel_space_boundaries.setdefault(self.product_index, []).append({
|
self.rel_space_boundaries.setdefault(self.product_index, []).append({
|
||||||
@@ -317,44 +331,46 @@ class IfcParser():
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if object.instance_type == 'COLLECTION' \
|
if obj.instance_type == 'COLLECTION' \
|
||||||
and self.is_a_rel_aggregates(self.get_ifc_class(object.instance_collection.name)):
|
and self.is_a_rel_aggregates(self.get_ifc_class(obj.instance_collection.name)):
|
||||||
self.rel_aggregates[self.product_index] = object.name
|
self.rel_aggregates[self.product_index] = obj.name
|
||||||
|
|
||||||
if 'rel_aggregates_relating_object' in selected_product['metadata']:
|
if 'rel_aggregates_relating_object' in selected_product['metadata']:
|
||||||
relating_object = selected_product['metadata']['rel_aggregates_relating_object']
|
relating_object = selected_product['metadata']['rel_aggregates_relating_object']
|
||||||
product['location'] = relating_object.matrix_world @ product['location']
|
inverted = relating_object.matrix_world.inverted()
|
||||||
product['up_axis'] = (relating_object.matrix_world.to_quaternion() @ object.matrix_world.to_quaternion()) @ Vector((0, 0, 1))
|
product['location'] = inverted @ product['location']
|
||||||
product['forward_axis'] = (relating_object.matrix_world.to_quaternion() @ object.matrix_world.to_quaternion()) @ Vector((1, 0, 0))
|
product['up_axis'] = self.get_axis(inverted @ obj.matrix_world, 2)
|
||||||
|
product['forward_axis'] = self.get_axis(inverted @ obj.matrix_world, 0)
|
||||||
self.aggregates.setdefault(relating_object.name, []).append(self.product_index)
|
self.aggregates.setdefault(relating_object.name, []).append(self.product_index)
|
||||||
|
|
||||||
if object.name in self.qtos:
|
if obj.name in self.qtos:
|
||||||
self.rel_defines_by_qto.setdefault(object.name, []).append(product)
|
self.rel_defines_by_qto.setdefault(obj.name, []).append(product)
|
||||||
|
|
||||||
for pset in object.BIMObjectProperties.psets:
|
for pset in obj.BIMObjectProperties.psets:
|
||||||
self.rel_defines_by_pset.setdefault(
|
self.rel_defines_by_pset.setdefault(
|
||||||
'{}/{}'.format(pset.name, pset.file), []).append(product)
|
'{}/{}'.format(pset.name, pset.file), []).append(product)
|
||||||
|
|
||||||
for document in object.BIMObjectProperties.documents:
|
for document in obj.BIMObjectProperties.documents:
|
||||||
self.rel_associates_document_object.setdefault(
|
self.rel_associates_document_object.setdefault(
|
||||||
document.file, []).append(product)
|
document.file, []).append(product)
|
||||||
|
|
||||||
for classification in object.BIMObjectProperties.classifications:
|
for classification in obj.BIMObjectProperties.classifications:
|
||||||
self.rel_associates_classification_object.setdefault(
|
self.rel_associates_classification_object.setdefault(
|
||||||
classification.identification, []).append(product)
|
classification.identification, []).append(product)
|
||||||
|
|
||||||
for key in object.keys():
|
for key in obj.keys():
|
||||||
if key[0:9] == 'Objective':
|
if key[0:9] == 'Objective':
|
||||||
self.rel_associates_constraint_objective_object.setdefault(
|
self.rel_associates_constraint_objective_object.setdefault(
|
||||||
object[key], []).append(product)
|
obj[key], []).append(product)
|
||||||
|
|
||||||
for slot in object.material_slots:
|
for slot in obj.material_slots:
|
||||||
if slot.link == 'OBJECT':
|
if slot.link == 'OBJECT':
|
||||||
continue
|
continue
|
||||||
if 'IsMaterialLayerSet' in object:
|
if 'IsMaterialLayerSet' in obj:
|
||||||
self.rel_associates_material_layer_set.setdefault(self.product_index, []).append(slot.material.name)
|
self.rel_associates_material_layer_set.setdefault(self.product_index, []).append(slot.material.name)
|
||||||
elif 'IsMaterialConstituentSet' in object:
|
elif 'IsMaterialConstituentSet' in obj:
|
||||||
self.rel_associates_material_constituent_set.setdefault(self.product_index, []).append(slot.material.name)
|
self.rel_associates_material_constituent_set.setdefault(self.product_index, []).append(
|
||||||
|
slot.material.name)
|
||||||
else:
|
else:
|
||||||
self.rel_associates_material.setdefault(slot.material.name, []).append(product)
|
self.rel_associates_material.setdefault(slot.material.name, []).append(product)
|
||||||
|
|
||||||
@@ -380,12 +396,12 @@ class IfcParser():
|
|||||||
if child.name == child_collection.name:
|
if child.name == child_collection.name:
|
||||||
return parent_collection
|
return parent_collection
|
||||||
|
|
||||||
def get_instances(self, object):
|
def get_instances(self, obj):
|
||||||
instances = []
|
instances = []
|
||||||
for m in object.modifiers:
|
for m in obj.modifiers:
|
||||||
if m.type == 'ARRAY':
|
if m.type == 'ARRAY':
|
||||||
array = ArrayModifier()
|
array = ArrayModifier()
|
||||||
world_rotation = object.matrix_world.decompose()[1]
|
world_rotation = obj.matrix_world.decompose()[1]
|
||||||
array.offset = world_rotation @ Vector(
|
array.offset = world_rotation @ Vector(
|
||||||
(m.constant_offset_displace[0], m.constant_offset_displace[1], m.constant_offset_displace[2]))
|
(m.constant_offset_displace[0], m.constant_offset_displace[1], m.constant_offset_displace[2]))
|
||||||
if m.fit_type == 'FIXED_COUNT':
|
if m.fit_type == 'FIXED_COUNT':
|
||||||
@@ -398,12 +414,12 @@ class IfcParser():
|
|||||||
def convert_selected_objects_into_products(self, objects_to_sort, metadata=None):
|
def convert_selected_objects_into_products(self, objects_to_sort, metadata=None):
|
||||||
if not metadata:
|
if not metadata:
|
||||||
metadata = {}
|
metadata = {}
|
||||||
for object in objects_to_sort:
|
for obj in objects_to_sort:
|
||||||
if not self.is_a_library(self.get_ifc_class(object.users_collection[0].name)):
|
if not self.is_a_library(self.get_ifc_class(obj.users_collection[0].name)):
|
||||||
self.selected_products.append({ 'raw': object, 'metadata': metadata })
|
self.selected_products.append({'raw': obj, 'metadata': metadata})
|
||||||
if object.instance_type == 'COLLECTION':
|
if obj.instance_type == 'COLLECTION':
|
||||||
self.convert_selected_objects_into_products(object.instance_collection.objects,
|
self.convert_selected_objects_into_products(obj.instance_collection.objects,
|
||||||
{'rel_aggregates_relating_object': object})
|
{'rel_aggregates_relating_object': obj})
|
||||||
|
|
||||||
def get_psets(self):
|
def get_psets(self):
|
||||||
psets = {}
|
psets = {}
|
||||||
@@ -579,22 +595,22 @@ class IfcParser():
|
|||||||
if not self.ifc_export_settings.has_representations:
|
if not self.ifc_export_settings.has_representations:
|
||||||
return results
|
return results
|
||||||
for product in self.selected_products + self.type_products:
|
for product in self.selected_products + self.type_products:
|
||||||
object = product['raw']
|
obj = product['raw']
|
||||||
if not object.data \
|
if not obj.data \
|
||||||
or object.data.name in results:
|
or obj.data.name in results:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.append_default_representation(object, results)
|
self.append_default_representation(obj, results)
|
||||||
self.append_representation_per_context(object, results)
|
self.append_representation_per_context(obj, results)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def append_default_representation(self, object, results):
|
def append_default_representation(self, obj, results):
|
||||||
if not self.is_mesh_context_sensitive(object.data.name):
|
if not self.is_mesh_context_sensitive(obj.data.name):
|
||||||
results['Model/Body/MODEL_VIEW/{}'.format(object.data.name)] = self.get_representation(
|
results['Model/Body/MODEL_VIEW/{}'.format(obj.data.name)] = self.get_representation(
|
||||||
object.data, object, 'Model', 'Body', 'MODEL_VIEW')
|
obj.data, obj, 'Model', 'Body', 'MODEL_VIEW')
|
||||||
|
|
||||||
def append_representation_per_context(self, object, results):
|
def append_representation_per_context(self, obj, results):
|
||||||
name = self.get_ifc_representation_name(object.data.name)
|
name = self.get_ifc_representation_name(obj.data.name)
|
||||||
for context in self.ifc_export_settings.context_tree:
|
for context in self.ifc_export_settings.context_tree:
|
||||||
for subcontext in context['subcontexts']:
|
for subcontext in context['subcontexts']:
|
||||||
for target_view in subcontext['target_views']:
|
for target_view in subcontext['target_views']:
|
||||||
@@ -604,13 +620,13 @@ class IfcParser():
|
|||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
results[mesh_name] = self.get_representation(
|
results[mesh_name] = self.get_representation(
|
||||||
mesh, object, context['name'], subcontext['name'], target_view)
|
mesh, obj, context['name'], subcontext['name'], target_view)
|
||||||
|
|
||||||
def get_representation(self, mesh, object, context, subcontext, target_view, is_generated=False):
|
def get_representation(self, mesh, obj, context, subcontext, target_view, is_generated=False):
|
||||||
return {
|
return {
|
||||||
'ifc': None,
|
'ifc': None,
|
||||||
'raw': mesh,
|
'raw': mesh,
|
||||||
'raw_object': object,
|
'raw_object': obj,
|
||||||
'context': context,
|
'context': context,
|
||||||
'subcontext': subcontext,
|
'subcontext': subcontext,
|
||||||
'target_view': target_view,
|
'target_view': target_view,
|
||||||
@@ -634,10 +650,10 @@ class IfcParser():
|
|||||||
if not self.ifc_export_settings.has_representations:
|
if not self.ifc_export_settings.has_representations:
|
||||||
return results
|
return results
|
||||||
for product in self.selected_products + self.type_products:
|
for product in self.selected_products + self.type_products:
|
||||||
object = product['raw']
|
obj = product['raw']
|
||||||
if not object.data:
|
if not obj.data:
|
||||||
continue
|
continue
|
||||||
for slot in object.material_slots:
|
for slot in obj.material_slots:
|
||||||
if slot.material.name in results \
|
if slot.material.name in results \
|
||||||
or slot.link == 'OBJECT':
|
or slot.link == 'OBJECT':
|
||||||
continue
|
continue
|
||||||
@@ -646,8 +662,8 @@ class IfcParser():
|
|||||||
'layer_ifc': None,
|
'layer_ifc': None,
|
||||||
'constituent_ifc': None,
|
'constituent_ifc': None,
|
||||||
'raw': slot.material,
|
'raw': slot.material,
|
||||||
'is_material_layer_set': True if 'IsMaterialLayerSet' in object.keys() else False,
|
'is_material_layer_set': True if 'IsMaterialLayerSet' in obj.keys() else False,
|
||||||
'is_material_constituent_set': True if 'IsMaterialConstituentSet' in object.keys() else False,
|
'is_material_constituent_set': True if 'IsMaterialConstituentSet' in obj.keys() else False,
|
||||||
'attributes': {'Name': slot.material.name},
|
'attributes': {'Name': slot.material.name},
|
||||||
'layer_attributes': {key[3:]: slot.material[key] for key in
|
'layer_attributes': {key[3:]: slot.material[key] for key in
|
||||||
slot.material.keys() if key[0:3] == 'Ifc'},
|
slot.material.keys() if key[0:3] == 'Ifc'},
|
||||||
@@ -661,10 +677,10 @@ class IfcParser():
|
|||||||
if not self.ifc_export_settings.has_representations:
|
if not self.ifc_export_settings.has_representations:
|
||||||
return results
|
return results
|
||||||
for product in self.selected_products + self.type_products:
|
for product in self.selected_products + self.type_products:
|
||||||
object = product['raw']
|
obj = product['raw']
|
||||||
if not object.data:
|
if not obj.data:
|
||||||
continue
|
continue
|
||||||
for slot in object.material_slots:
|
for slot in obj.material_slots:
|
||||||
if not self.ifc_export_settings.should_export_all_materials_as_styled_items:
|
if not self.ifc_export_settings.should_export_all_materials_as_styled_items:
|
||||||
if slot.material.name in results \
|
if slot.material.name in results \
|
||||||
or slot.link == 'DATA':
|
or slot.link == 'DATA':
|
||||||
@@ -682,19 +698,19 @@ class IfcParser():
|
|||||||
return {}
|
return {}
|
||||||
results = {}
|
results = {}
|
||||||
for product in self.selected_products + self.type_products:
|
for product in self.selected_products + self.type_products:
|
||||||
object = product['raw']
|
obj = product['raw']
|
||||||
if not object.data:
|
if not obj.data:
|
||||||
continue
|
continue
|
||||||
for property in object.keys():
|
for property in obj.keys():
|
||||||
if property[0:4] != 'Qto_':
|
if property[0:4] != 'Qto_':
|
||||||
continue
|
continue
|
||||||
results[object.name] = {
|
results[obj.name] = {
|
||||||
'ifc': None,
|
'ifc': None,
|
||||||
'raw': object,
|
'raw': obj,
|
||||||
'class': property,
|
'class': property,
|
||||||
'attributes': {
|
'attributes': {
|
||||||
'Name': property,
|
'Name': property,
|
||||||
'MethodOfMeasurement': object[property]
|
'MethodOfMeasurement': obj[property]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results
|
return results
|
||||||
@@ -703,48 +719,49 @@ class IfcParser():
|
|||||||
results = []
|
results = []
|
||||||
index = 0
|
index = 0
|
||||||
for library in self.libraries:
|
for library in self.libraries:
|
||||||
for object in library['raw'].objects:
|
for obj in library['raw'].objects:
|
||||||
if not self.is_a_type(self.get_ifc_class(object.name)):
|
if not self.is_a_type(self.get_ifc_class(obj.name)):
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
type = {
|
type = {
|
||||||
'ifc': None,
|
'ifc': None,
|
||||||
'raw': object,
|
'raw': obj,
|
||||||
'location': object.translation,
|
'location': obj.translation,
|
||||||
'up_axis': object.matrix_world.to_quaternion() @ Vector((0, 0, 1)),
|
'up_axis': obj.matrix_world.to_quaternion() @ Vector((0, 0, 1)),
|
||||||
'forward_axis': object.matrix_world.to_quaternion() @ Vector((1, 0, 0)),
|
'forward_axis': obj.matrix_world.to_quaternion() @ Vector((1, 0, 0)),
|
||||||
'psets': ['{}/{}'.format(pset.name, pset.file) for pset in
|
'psets': ['{}/{}'.format(pset.name, pset.file) for pset in
|
||||||
object.BIMObjectProperties.psets],
|
obj.BIMObjectProperties.psets],
|
||||||
'class': self.get_ifc_class(object.name),
|
'class': self.get_ifc_class(obj.name),
|
||||||
'representations': self.get_object_representation_names(object),
|
'representations': self.get_object_representation_names(obj),
|
||||||
'attributes': self.get_object_attributes(object)
|
'attributes': self.get_object_attributes(obj)
|
||||||
}
|
}
|
||||||
results.append(type)
|
results.append(type)
|
||||||
library['rel_declares_type_products'].append(index)
|
library['rel_declares_type_products'].append(index)
|
||||||
|
|
||||||
for key in object.keys():
|
for key in obj.keys():
|
||||||
if key[0:3] == 'Doc':
|
if key[0:3] == 'Doc':
|
||||||
self.rel_associates_document_type.setdefault(
|
self.rel_associates_document_type.setdefault(
|
||||||
object[key], []).append(type)
|
obj[key], []).append(type)
|
||||||
elif key[0:5] == 'Class':
|
elif key[0:5] == 'Class':
|
||||||
self.rel_associates_classification_type.setdefault(
|
self.rel_associates_classification_type.setdefault(
|
||||||
object[key], []).append(type)
|
obj[key], []).append(type)
|
||||||
elif key[0:9] == 'Objective':
|
elif key[0:9] == 'Objective':
|
||||||
self.rel_associates_constraint_objective_type.setdefault(
|
self.rel_associates_constraint_objective_type.setdefault(
|
||||||
object[key], []).append(type)
|
obj[key], []).append(type)
|
||||||
|
|
||||||
index += 1
|
index += 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.ifc_export_settings.logger.error('The type product "{}" could not be parsed: {}'.format(object.name, e.args))
|
self.ifc_export_settings.logger.error(
|
||||||
|
'The type product "{}" could not be parsed: {}'.format(obj.name, e.args))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def get_object_representation_names(self, object):
|
def get_object_representation_names(self, obj):
|
||||||
names = []
|
names = []
|
||||||
if not object.data:
|
if not obj.data:
|
||||||
return names
|
return names
|
||||||
if not self.is_mesh_context_sensitive(object.data.name):
|
if not self.is_mesh_context_sensitive(obj.data.name):
|
||||||
names.append('Model/Body/MODEL_VIEW/{}'.format(object.data.name))
|
names.append('Model/Body/MODEL_VIEW/{}'.format(obj.data.name))
|
||||||
name = self.get_ifc_representation_name(object.data.name)
|
name = self.get_ifc_representation_name(obj.data.name)
|
||||||
for context in self.ifc_export_settings.context_tree:
|
for context in self.ifc_export_settings.context_tree:
|
||||||
for subcontext in context['subcontexts']:
|
for subcontext in context['subcontexts']:
|
||||||
for target_view in subcontext['target_views']:
|
for target_view in subcontext['target_views']:
|
||||||
@@ -787,7 +804,8 @@ class IfcParser():
|
|||||||
try:
|
try:
|
||||||
return name.split('/')[1]
|
return name.split('/')[1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
self.ifc_export_settings.logger.error('Name "{}" does not follow the format of "IfcClass/Name"'.format(name))
|
self.ifc_export_settings.logger.error(
|
||||||
|
'Name "{}" does not follow the format of "IfcClass/Name"'.format(name))
|
||||||
|
|
||||||
def is_a_spatial_structure_element(self, class_name):
|
def is_a_spatial_structure_element(self, class_name):
|
||||||
# We assume that any collection we can't identify is a spatial structure
|
# We assume that any collection we can't identify is a spatial structure
|
||||||
@@ -808,6 +826,7 @@ class IfcParser():
|
|||||||
def is_a_type(self, class_name):
|
def is_a_type(self, class_name):
|
||||||
return class_name[0:3] == 'Ifc' and class_name[-4:] == 'Type'
|
return class_name[0:3] == 'Ifc' and class_name[-4:] == 'Type'
|
||||||
|
|
||||||
|
|
||||||
class IfcExporter():
|
class IfcExporter():
|
||||||
def __init__(self, ifc_export_settings, ifc_schema, ifc_parser, qto_calculator):
|
def __init__(self, ifc_export_settings, ifc_schema, ifc_parser, qto_calculator):
|
||||||
self.template_file = '{}template.ifc'.format(ifc_export_settings.schema_dir)
|
self.template_file = '{}template.ifc'.format(ifc_export_settings.schema_dir)
|
||||||
@@ -963,7 +982,8 @@ class IfcExporter():
|
|||||||
|
|
||||||
def create_classification_references(self):
|
def create_classification_references(self):
|
||||||
for reference in self.ifc_parser.classification_references.values():
|
for reference in self.ifc_parser.classification_references.values():
|
||||||
reference['attributes']['ReferencedSource'] = self.ifc_parser.classifications[reference['referenced_source']]['ifc']
|
reference['attributes']['ReferencedSource'] = \
|
||||||
|
self.ifc_parser.classifications[reference['referenced_source']]['ifc']
|
||||||
reference['ifc'] = self.file.create_entity(
|
reference['ifc'] = self.file.create_entity(
|
||||||
'IfcClassificationReference', **reference['attributes'])
|
'IfcClassificationReference', **reference['attributes'])
|
||||||
|
|
||||||
@@ -1097,7 +1117,8 @@ class IfcExporter():
|
|||||||
|
|
||||||
def create_type_products(self):
|
def create_type_products(self):
|
||||||
for product in self.ifc_parser.type_products:
|
for product in self.ifc_parser.type_products:
|
||||||
placement = self.create_ifc_axis_2_placement_3d(product['location'], product['up_axis'], product['forward_axis'])
|
placement = self.create_ifc_axis_2_placement_3d(product['location'], product['up_axis'],
|
||||||
|
product['forward_axis'])
|
||||||
|
|
||||||
if product['representations']:
|
if product['representations']:
|
||||||
maps = []
|
maps = []
|
||||||
@@ -1114,16 +1135,20 @@ class IfcExporter():
|
|||||||
if product['class'] == 'IfcDoorType' \
|
if product['class'] == 'IfcDoorType' \
|
||||||
and product['attributes']['Name'] in self.ifc_parser.door_attributes:
|
and product['attributes']['Name'] in self.ifc_parser.door_attributes:
|
||||||
self.add_predefined_attributes_to_type_product(product,
|
self.add_predefined_attributes_to_type_product(product,
|
||||||
self.ifc_parser.door_attributes[product['attributes']['Name']])
|
self.ifc_parser.door_attributes[
|
||||||
|
product['attributes']['Name']])
|
||||||
elif product['class'] == 'IfcWindowType' \
|
elif product['class'] == 'IfcWindowType' \
|
||||||
and product['attributes']['Name'] in self.ifc_parser.window_attributes:
|
and product['attributes']['Name'] in self.ifc_parser.window_attributes:
|
||||||
self.add_predefined_attributes_to_type_product(product,
|
self.add_predefined_attributes_to_type_product(product,
|
||||||
self.ifc_parser.window_attributes[product['attributes']['Name']])
|
self.ifc_parser.window_attributes[
|
||||||
|
product['attributes']['Name']])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
product['ifc'] = self.file.create_entity(product['class'], **product['attributes'])
|
product['ifc'] = self.file.create_entity(product['class'], **product['attributes'])
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
self.ifc_export_settings.logger.error('The type product "{}/{}" could not be created: {}'.format(product['class'], product['attributes']['Name'], e.args))
|
self.ifc_export_settings.logger.error(
|
||||||
|
'The type product "{}/{}" could not be created: {}'.format(product['class'],
|
||||||
|
product['attributes']['Name'], e.args))
|
||||||
|
|
||||||
def add_predefined_attributes_to_type_product(self, product, attributes):
|
def add_predefined_attributes_to_type_product(self, product, attributes):
|
||||||
self.create_predefined_attributes(attributes)
|
self.create_predefined_attributes(attributes)
|
||||||
@@ -1134,7 +1159,8 @@ class IfcExporter():
|
|||||||
def create_predefined_attributes(self, attributes):
|
def create_predefined_attributes(self, attributes):
|
||||||
for attribute in attributes:
|
for attribute in attributes:
|
||||||
attribute['ifc'] = self.file.create_entity(attribute['pset_name'],
|
attribute['ifc'] = self.file.create_entity(attribute['pset_name'],
|
||||||
**{k: float(v) if v.replace('.', '', 1).isdigit() else v for k, v in attribute['raw'].items()})
|
**{k: float(v) if v.replace('.', '', 1).isdigit() else v for k, v
|
||||||
|
in attribute['raw'].items()})
|
||||||
|
|
||||||
def relate_definitions_to_contexts(self):
|
def relate_definitions_to_contexts(self):
|
||||||
for library in self.ifc_parser.libraries:
|
for library in self.ifc_parser.libraries:
|
||||||
@@ -1146,7 +1172,8 @@ class IfcExporter():
|
|||||||
def relate_objects_to_objects(self):
|
def relate_objects_to_objects(self):
|
||||||
for relating_object, related_objects_reference in self.ifc_parser.rel_aggregates.items():
|
for relating_object, related_objects_reference in self.ifc_parser.rel_aggregates.items():
|
||||||
relating_object = self.ifc_parser.products[relating_object]
|
relating_object = self.ifc_parser.products[relating_object]
|
||||||
related_objects = [ self.ifc_parser.products[o]['ifc'] for o in self.ifc_parser.aggregates[related_objects_reference] ]
|
related_objects = [self.ifc_parser.products[o]['ifc'] for o in
|
||||||
|
self.ifc_parser.aggregates[related_objects_reference]]
|
||||||
self.file.createIfcRelAggregates(
|
self.file.createIfcRelAggregates(
|
||||||
ifcopenshell.guid.new(), self.owner_history, relating_object['attributes']['Name'], None,
|
ifcopenshell.guid.new(), self.owner_history, relating_object['attributes']['Name'], None,
|
||||||
relating_object['ifc'], related_objects)
|
relating_object['ifc'], related_objects)
|
||||||
@@ -1260,7 +1287,8 @@ class IfcExporter():
|
|||||||
|
|
||||||
def create_product(self, product):
|
def create_product(self, product):
|
||||||
if product['relating_structure']:
|
if product['relating_structure']:
|
||||||
placement_rel_to = self.ifc_parser.spatial_structure_elements[product['relating_structure']]['ifc'].ObjectPlacement
|
placement_rel_to = self.ifc_parser.spatial_structure_elements[product['relating_structure']][
|
||||||
|
'ifc'].ObjectPlacement
|
||||||
elif product['relating_host'] is not None:
|
elif product['relating_host'] is not None:
|
||||||
placement_rel_to = self.ifc_parser.products[product['relating_host']]['ifc'].ObjectPlacement
|
placement_rel_to = self.ifc_parser.products[product['relating_host']]['ifc'].ObjectPlacement
|
||||||
else:
|
else:
|
||||||
@@ -1286,7 +1314,9 @@ class IfcExporter():
|
|||||||
try:
|
try:
|
||||||
product['ifc'] = self.file.create_entity(product['class'], **product['attributes'])
|
product['ifc'] = self.file.create_entity(product['class'], **product['attributes'])
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
self.ifc_export_settings.logger.error('The product "{}/{}" could not be created: {}'.format(product['class'], product['attributes']['Name'], e.args))
|
self.ifc_export_settings.logger.error(
|
||||||
|
'The product "{}/{}" could not be created: {}'.format(product['class'], product['attributes']['Name'],
|
||||||
|
e.args))
|
||||||
|
|
||||||
def get_product_attribute_type(self, product_class, attribute_name):
|
def get_product_attribute_type(self, product_class, attribute_name):
|
||||||
element_schema = self.ifc_schema.elements[product_class]
|
element_schema = self.ifc_schema.elements[product_class]
|
||||||
@@ -1300,34 +1330,35 @@ class IfcExporter():
|
|||||||
def get_product_shape(self, product):
|
def get_product_shape(self, product):
|
||||||
try:
|
try:
|
||||||
shape = self.file.createIfcProductDefinitionShape(None, None,
|
shape = self.file.createIfcProductDefinitionShape(None, None,
|
||||||
[self.ifc_parser.representations[p]['ifc'] for p in product['representations']])
|
[self.ifc_parser.representations[p]['ifc'] for p in
|
||||||
|
product['representations']])
|
||||||
except:
|
except:
|
||||||
shape = None
|
shape = None
|
||||||
return shape
|
return shape
|
||||||
|
|
||||||
def calculate_quantities(self, qto_name, object):
|
def calculate_quantities(self, qto_name, obj):
|
||||||
quantities = []
|
quantities = []
|
||||||
for index, vg in enumerate(object.vertex_groups):
|
for index, vg in enumerate(obj.vertex_groups):
|
||||||
if qto_name not in vg.name:
|
if qto_name not in vg.name:
|
||||||
continue
|
continue
|
||||||
if 'length' in vg.name.lower():
|
if 'length' in vg.name.lower():
|
||||||
quantity = float(self.qto_calculator.get_length(object, index))
|
quantity = float(self.qto_calculator.get_length(obj, index))
|
||||||
quantities.append(self.file.createIfcQuantityLength(
|
quantities.append(self.file.createIfcQuantityLength(
|
||||||
vg.name.split('/')[1], None,
|
vg.name.split('/')[1], None,
|
||||||
self.ifc_parser.units['length']['ifc'], quantity))
|
self.ifc_parser.units['length']['ifc'], quantity))
|
||||||
elif 'area' in vg.name.lower():
|
elif 'area' in vg.name.lower():
|
||||||
quantity = float(self.qto_calculator.get_area(object, index))
|
quantity = float(self.qto_calculator.get_area(obj, index))
|
||||||
quantities.append(self.file.createIfcQuantityArea(
|
quantities.append(self.file.createIfcQuantityArea(
|
||||||
vg.name.split('/')[1], None,
|
vg.name.split('/')[1], None,
|
||||||
self.ifc_parser.units['area']['ifc'], quantity))
|
self.ifc_parser.units['area']['ifc'], quantity))
|
||||||
elif 'volume' in vg.name.lower():
|
elif 'volume' in vg.name.lower():
|
||||||
quantity = float(self.qto_calculator.get_volume(object, index))
|
quantity = float(self.qto_calculator.get_volume(obj, index))
|
||||||
quantities.append(self.file.createIfcQuantityVolume(
|
quantities.append(self.file.createIfcQuantityVolume(
|
||||||
vg.name.split('/')[1], None,
|
vg.name.split('/')[1], None,
|
||||||
self.ifc_parser.units['volume']['ifc'], quantity))
|
self.ifc_parser.units['volume']['ifc'], quantity))
|
||||||
if not quantity:
|
if not quantity:
|
||||||
self.ifc_export_settings.logger.warning('The calculated quantity {} for {} is zero.'.format(
|
self.ifc_export_settings.logger.warning('The calculated quantity {} for {} is zero.'.format(
|
||||||
vg.name, object.name))
|
vg.name, obj.name))
|
||||||
return quantities
|
return quantities
|
||||||
|
|
||||||
def create_ifc_axis_2_placement_3d(self, point, up, forward):
|
def create_ifc_axis_2_placement_3d(self, point, up, forward):
|
||||||
@@ -1359,13 +1390,14 @@ class IfcExporter():
|
|||||||
return self.create_solid_representation(representation)
|
return self.create_solid_representation(representation)
|
||||||
|
|
||||||
def create_box_representation(self, representation):
|
def create_box_representation(self, representation):
|
||||||
object = representation['raw_object']
|
obj = representation['raw_object']
|
||||||
bounding_box = self.file.createIfcBoundingBox(
|
bounding_box = self.file.createIfcBoundingBox(
|
||||||
self.create_cartesian_point(
|
self.create_cartesian_point(
|
||||||
object.bound_box[0][0], object.bound_box[0][1], object.bound_box[0][2]),
|
obj.bound_box[0][0], obj.bound_box[0][1], obj.bound_box[0][2]),
|
||||||
object.dimensions[0], object.dimensions[1], object.dimensions[2])
|
obj.dimensions[0], obj.dimensions[1], obj.dimensions[2])
|
||||||
return self.file.createIfcShapeRepresentation(
|
return self.file.createIfcShapeRepresentation(
|
||||||
self.ifc_rep_context[representation['context']][representation['subcontext']][representation['target_view']]['ifc'],
|
self.ifc_rep_context[representation['context']][representation['subcontext']][
|
||||||
|
representation['target_view']]['ifc'],
|
||||||
representation['subcontext'], 'BoundingBox', [bounding_box])
|
representation['subcontext'], 'BoundingBox', [bounding_box])
|
||||||
|
|
||||||
def create_cog_representation(self, representation):
|
def create_cog_representation(self, representation):
|
||||||
@@ -1373,7 +1405,8 @@ class IfcExporter():
|
|||||||
cog = self.create_cartesian_point(
|
cog = self.create_cartesian_point(
|
||||||
mesh.vertices[0].co.x, mesh.vertices[0].co.y, mesh.vertices[0].co.z)
|
mesh.vertices[0].co.x, mesh.vertices[0].co.y, mesh.vertices[0].co.z)
|
||||||
return self.file.createIfcShapeRepresentation(
|
return self.file.createIfcShapeRepresentation(
|
||||||
self.ifc_rep_context[representation['context']][representation['subcontext']][representation['target_view']]['ifc'],
|
self.ifc_rep_context[representation['context']][representation['subcontext']][
|
||||||
|
representation['target_view']]['ifc'],
|
||||||
representation['subcontext'], 'BoundingBox', [cog])
|
representation['subcontext'], 'BoundingBox', [cog])
|
||||||
|
|
||||||
def create_wireframe_representation(self, representation):
|
def create_wireframe_representation(self, representation):
|
||||||
@@ -1383,7 +1416,8 @@ class IfcExporter():
|
|||||||
self.ifc_edges.append(self.file.createIfcPolyline([
|
self.ifc_edges.append(self.file.createIfcPolyline([
|
||||||
self.ifc_vertices[v] for v in edge.vertices]))
|
self.ifc_vertices[v] for v in edge.vertices]))
|
||||||
return self.file.createIfcShapeRepresentation(
|
return self.file.createIfcShapeRepresentation(
|
||||||
self.ifc_rep_context[representation['context']][representation['subcontext']][representation['target_view']]['ifc'],
|
self.ifc_rep_context[representation['context']][representation['subcontext']][
|
||||||
|
representation['target_view']]['ifc'],
|
||||||
representation['subcontext'], 'Curve',
|
representation['subcontext'], 'Curve',
|
||||||
self.ifc_edges)
|
self.ifc_edges)
|
||||||
|
|
||||||
@@ -1405,7 +1439,8 @@ class IfcExporter():
|
|||||||
self.ifc_vertices[i] for i in loop_vertex_indices]))
|
self.ifc_vertices[i] for i in loop_vertex_indices]))
|
||||||
geometric_curve_set = self.file.createIfcGeometricCurveSet(loops)
|
geometric_curve_set = self.file.createIfcGeometricCurveSet(loops)
|
||||||
return self.file.createIfcShapeRepresentation(
|
return self.file.createIfcShapeRepresentation(
|
||||||
self.ifc_rep_context[representation['context']][representation['subcontext']][representation['target_view']]['ifc'],
|
self.ifc_rep_context[representation['context']][representation['subcontext']][
|
||||||
|
representation['target_view']]['ifc'],
|
||||||
representation['subcontext'], 'GeometricCurveSet', [geometric_curve_set])
|
representation['subcontext'], 'GeometricCurveSet', [geometric_curve_set])
|
||||||
|
|
||||||
# https://medium.com/@behreajj/scripting-curves-in-blender-with-python-c487097efd13
|
# https://medium.com/@behreajj/scripting-curves-in-blender-with-python-c487097efd13
|
||||||
@@ -1429,7 +1464,8 @@ class IfcExporter():
|
|||||||
def create_curve_representation(self, representation):
|
def create_curve_representation(self, representation):
|
||||||
# TODO: support unclosed surfaces
|
# TODO: support unclosed surfaces
|
||||||
swept_area = self.file.createIfcArbitraryClosedProfileDef('AREA', None,
|
swept_area = self.file.createIfcArbitraryClosedProfileDef('AREA', None,
|
||||||
self.create_curve(representation['raw'].bevel_object.data))
|
self.create_curve(
|
||||||
|
representation['raw'].bevel_object.data))
|
||||||
swept_area_solids = []
|
swept_area_solids = []
|
||||||
for spline in representation['raw'].splines:
|
for spline in representation['raw'].splines:
|
||||||
direction = spline.bezier_points[1].co - spline.bezier_points[0].co
|
direction = spline.bezier_points[1].co - spline.bezier_points[0].co
|
||||||
@@ -1459,7 +1495,8 @@ class IfcExporter():
|
|||||||
# swept_area, self.origin, self.create_curve(representation['raw']),
|
# swept_area, self.origin, self.create_curve(representation['raw']),
|
||||||
# 0., 1., self.file.createIfcDirection((0.0, -1.0, 0.0)))
|
# 0., 1., self.file.createIfcDirection((0.0, -1.0, 0.0)))
|
||||||
return self.file.createIfcShapeRepresentation(
|
return self.file.createIfcShapeRepresentation(
|
||||||
self.ifc_rep_context[representation['context']][representation['subcontext']][representation['target_view']]['ifc'],
|
self.ifc_rep_context[representation['context']][representation['subcontext']][
|
||||||
|
representation['target_view']]['ifc'],
|
||||||
representation['subcontext'], 'AdvancedSweptSolid',
|
representation['subcontext'], 'AdvancedSweptSolid',
|
||||||
swept_area_solids)
|
swept_area_solids)
|
||||||
|
|
||||||
@@ -1474,23 +1511,23 @@ class IfcExporter():
|
|||||||
return self.file.createIfcPolyline(points)
|
return self.file.createIfcPolyline(points)
|
||||||
|
|
||||||
def create_swept_solid_representation(self, representation):
|
def create_swept_solid_representation(self, representation):
|
||||||
object = representation['raw_object']
|
obj = representation['raw_object']
|
||||||
mesh = representation['raw']
|
mesh = representation['raw']
|
||||||
items = []
|
items = []
|
||||||
for swept_solid in mesh.BIMMeshProperties.swept_solids:
|
for swept_solid in mesh.BIMMeshProperties.swept_solids:
|
||||||
extrusion_edge = self.get_edges_in_v_indices(object, json.loads(swept_solid.extrusion))[0]
|
extrusion_edge = self.get_edges_in_v_indices(obj, json.loads(swept_solid.extrusion))[0]
|
||||||
|
|
||||||
inner_curves = []
|
inner_curves = []
|
||||||
if swept_solid.inner_curves:
|
if swept_solid.inner_curves:
|
||||||
for indices in json.loads(swept_solid.inner_curves):
|
for indices in json.loads(swept_solid.inner_curves):
|
||||||
loop = self.get_loop_from_v_indices(object, indices)
|
loop = self.get_loop_from_v_indices(obj, indices)
|
||||||
curve_ucs = self.get_curve_profile_coordinate_system(object, loop)
|
curve_ucs = self.get_curve_profile_coordinate_system(obj, loop)
|
||||||
inner_curves.append(
|
inner_curves.append(
|
||||||
self.create_polyline_from_loop(object, loop, curve_ucs))
|
self.create_polyline_from_loop(obj, loop, curve_ucs))
|
||||||
|
|
||||||
outer_curve_loop = self.get_loop_from_v_indices(object, json.loads(swept_solid.outer_curve))
|
outer_curve_loop = self.get_loop_from_v_indices(obj, json.loads(swept_solid.outer_curve))
|
||||||
curve_ucs = self.get_curve_profile_coordinate_system(object, outer_curve_loop)
|
curve_ucs = self.get_curve_profile_coordinate_system(obj, outer_curve_loop)
|
||||||
outer_curve = self.create_polyline_from_loop(object, outer_curve_loop, curve_ucs)
|
outer_curve = self.create_polyline_from_loop(obj, outer_curve_loop, curve_ucs)
|
||||||
|
|
||||||
if inner_curves:
|
if inner_curves:
|
||||||
curve = self.file.createIfcArbitraryProfileDefWithVoids('AREA', None,
|
curve = self.file.createIfcArbitraryProfileDefWithVoids('AREA', None,
|
||||||
@@ -1498,7 +1535,7 @@ class IfcExporter():
|
|||||||
else:
|
else:
|
||||||
curve = self.file.createIfcArbitraryClosedProfileDef('AREA', None, outer_curve)
|
curve = self.file.createIfcArbitraryClosedProfileDef('AREA', None, outer_curve)
|
||||||
|
|
||||||
direction = self.get_extrusion_direction(object, outer_curve_loop, extrusion_edge, curve_ucs)
|
direction = self.get_extrusion_direction(obj, outer_curve_loop, extrusion_edge, curve_ucs)
|
||||||
unit_direction = direction.normalized()
|
unit_direction = direction.normalized()
|
||||||
position = self.create_ifc_axis_2_placement_3d(
|
position = self.create_ifc_axis_2_placement_3d(
|
||||||
curve_ucs['center'], curve_ucs['z_axis'], curve_ucs['x_axis'])
|
curve_ucs['center'], curve_ucs['z_axis'], curve_ucs['x_axis'])
|
||||||
@@ -1508,7 +1545,8 @@ class IfcExporter():
|
|||||||
unit_direction.x, unit_direction.y, unit_direction.z)),
|
unit_direction.x, unit_direction.y, unit_direction.z)),
|
||||||
self.convert_si_to_unit(direction.length)))
|
self.convert_si_to_unit(direction.length)))
|
||||||
return self.file.createIfcShapeRepresentation(
|
return self.file.createIfcShapeRepresentation(
|
||||||
self.ifc_rep_context[representation['context']][representation['subcontext']][representation['target_view']]['ifc'],
|
self.ifc_rep_context[representation['context']][representation['subcontext']][
|
||||||
|
representation['target_view']]['ifc'],
|
||||||
representation['subcontext'], 'SweptSolid', items)
|
representation['subcontext'], 'SweptSolid', items)
|
||||||
|
|
||||||
def get_start_and_end_of_extrusion(self, profile_points, extrusion_edge):
|
def get_start_and_end_of_extrusion(self, profile_points, extrusion_edge):
|
||||||
@@ -1516,17 +1554,17 @@ class IfcExporter():
|
|||||||
return (extrusion_edge.vertices[0], extrusion_edge.vertices[1])
|
return (extrusion_edge.vertices[0], extrusion_edge.vertices[1])
|
||||||
return (extrusion_edge.vertices[1], extrusion_edge.vertices[0])
|
return (extrusion_edge.vertices[1], extrusion_edge.vertices[0])
|
||||||
|
|
||||||
def get_curve_profile_coordinate_system(self, object, loop):
|
def get_curve_profile_coordinate_system(self, obj, loop):
|
||||||
profile_face = bpy.data.meshes.new('profile_face')
|
profile_face = bpy.data.meshes.new('profile_face')
|
||||||
profile_verts = [(
|
profile_verts = [(
|
||||||
object.data.vertices[p].co.x,
|
obj.data.vertices[p].co.x,
|
||||||
object.data.vertices[p].co.y,
|
obj.data.vertices[p].co.y,
|
||||||
object.data.vertices[p].co.z
|
obj.data.vertices[p].co.z
|
||||||
) for p in loop]
|
) for p in loop]
|
||||||
profile_faces = [tuple(range(0, len(profile_verts)))]
|
profile_faces = [tuple(range(0, len(profile_verts)))]
|
||||||
profile_face.from_pydata(profile_verts, [], profile_faces)
|
profile_face.from_pydata(profile_verts, [], profile_faces)
|
||||||
center = profile_face.polygons[0].center
|
center = profile_face.polygons[0].center
|
||||||
x_axis = (object.data.vertices[loop[0]].co - center).normalized()
|
x_axis = (obj.data.vertices[loop[0]].co - center).normalized()
|
||||||
z_axis = profile_face.polygons[0].normal.normalized()
|
z_axis = profile_face.polygons[0].normal.normalized()
|
||||||
y_axis = z_axis.cross(x_axis).normalized()
|
y_axis = z_axis.cross(x_axis).normalized()
|
||||||
matrix = Matrix((x_axis, y_axis, z_axis))
|
matrix = Matrix((x_axis, y_axis, z_axis))
|
||||||
@@ -1539,27 +1577,28 @@ class IfcExporter():
|
|||||||
'matrix': matrix.to_4x4() @ Matrix.Translation(-center)
|
'matrix': matrix.to_4x4() @ Matrix.Translation(-center)
|
||||||
}
|
}
|
||||||
|
|
||||||
def create_polyline_from_loop(self, object, loop, curve_ucs):
|
def create_polyline_from_loop(self, obj, loop, curve_ucs):
|
||||||
points = []
|
points = []
|
||||||
for point in loop:
|
for point in loop:
|
||||||
transformed_point = curve_ucs['matrix'] @ object.data.vertices[point].co
|
transformed_point = curve_ucs['matrix'] @ obj.data.vertices[point].co
|
||||||
points.append(self.create_cartesian_point(
|
points.append(self.create_cartesian_point(
|
||||||
transformed_point.x, transformed_point.y))
|
transformed_point.x, transformed_point.y))
|
||||||
points.append(points[0])
|
points.append(points[0])
|
||||||
return self.file.createIfcPolyline(points)
|
return self.file.createIfcPolyline(points)
|
||||||
|
|
||||||
def get_extrusion_direction(self, object, outer_curve_loop, extrusion_edge, curve_ucs):
|
def get_extrusion_direction(self, obj, outer_curve_loop, extrusion_edge, curve_ucs):
|
||||||
start, end = self.get_start_and_end_of_extrusion(outer_curve_loop, extrusion_edge)
|
start, end = self.get_start_and_end_of_extrusion(outer_curve_loop, extrusion_edge)
|
||||||
return curve_ucs['matrix'] @ (curve_ucs['center'] + (object.data.vertices[end].co - object.data.vertices[start].co))
|
return curve_ucs['matrix'] @ (
|
||||||
|
curve_ucs['center'] + (obj.data.vertices[end].co - obj.data.vertices[start].co))
|
||||||
|
|
||||||
def get_loop_from_v_indices(self, object, indices):
|
def get_loop_from_v_indices(self, obj, indices):
|
||||||
edges = self.get_edges_in_v_indices(object, indices)
|
edges = self.get_edges_in_v_indices(obj, indices)
|
||||||
loop = self.get_loop_from_edges(edges)
|
loop = self.get_loop_from_edges(edges)
|
||||||
loop.pop(-1)
|
loop.pop(-1)
|
||||||
return loop
|
return loop
|
||||||
|
|
||||||
def get_edges_in_v_indices(self, object, indices):
|
def get_edges_in_v_indices(self, obj, indices):
|
||||||
return [ e for e in object.data.edges if (
|
return [e for e in obj.data.edges if (
|
||||||
e.vertices[0] in indices and e.vertices[1] in indices)]
|
e.vertices[0] in indices and e.vertices[1] in indices)]
|
||||||
|
|
||||||
def get_loop_from_edges(self, edges):
|
def get_loop_from_edges(self, edges):
|
||||||
@@ -1606,7 +1645,8 @@ class IfcExporter():
|
|||||||
self.file.createIfcPolyLoop([self.ifc_vertices[vertice] for vertice in polygon.vertices]),
|
self.file.createIfcPolyLoop([self.ifc_vertices[vertice] for vertice in polygon.vertices]),
|
||||||
True)]))
|
True)]))
|
||||||
return self.file.createIfcShapeRepresentation(
|
return self.file.createIfcShapeRepresentation(
|
||||||
self.ifc_rep_context[representation['context']][representation['subcontext']][representation['target_view']]['ifc'],
|
self.ifc_rep_context[representation['context']][representation['subcontext']][
|
||||||
|
representation['target_view']]['ifc'],
|
||||||
representation['subcontext'], 'Brep',
|
representation['subcontext'], 'Brep',
|
||||||
[self.file.createIfcFacetedBrep(self.file.createIfcClosedShell(self.ifc_faces))])
|
[self.file.createIfcFacetedBrep(self.file.createIfcClosedShell(self.ifc_faces))])
|
||||||
|
|
||||||
@@ -1623,7 +1663,6 @@ class IfcExporter():
|
|||||||
z = self.convert_si_to_unit(z)
|
z = self.convert_si_to_unit(z)
|
||||||
return self.file.createIfcCartesianPoint((x, y, z))
|
return self.file.createIfcCartesianPoint((x, y, z))
|
||||||
|
|
||||||
|
|
||||||
def relate_voids_elements(self):
|
def relate_voids_elements(self):
|
||||||
for relate_object, related_voids in self.ifc_parser.rel_voids_elements.items():
|
for relate_object, related_voids in self.ifc_parser.rel_voids_elements.items():
|
||||||
for related_void in related_voids:
|
for related_void in related_voids:
|
||||||
@@ -1768,6 +1807,7 @@ class IfcExporter():
|
|||||||
def convert_si_to_unit(self, co):
|
def convert_si_to_unit(self, co):
|
||||||
return co / self.ifc_parser.unit_scale
|
return co / self.ifc_parser.unit_scale
|
||||||
|
|
||||||
|
|
||||||
class IfcExportSettings:
|
class IfcExportSettings:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.logger = None
|
self.logger = None
|
||||||
@@ -1779,7 +1819,8 @@ class IfcExportSettings:
|
|||||||
self.contexts = ['Model', 'Plan']
|
self.contexts = ['Model', 'Plan']
|
||||||
self.subcontexts = ['Axis', 'FootPrint', 'Reference', 'Body', 'Clearance', 'CoG', 'SurveyPoints']
|
self.subcontexts = ['Axis', 'FootPrint', 'Reference', 'Body', 'Clearance', 'CoG', 'SurveyPoints']
|
||||||
self.generated_subcontexts = ['Box']
|
self.generated_subcontexts = ['Box']
|
||||||
self.target_views = ['GRAPH_VIEW', 'SKETCH_VIEW', 'MODEL_VIEW', 'PLAN_VIEW', 'REFLECTED_PLAN_VIEW', 'SECTION_VIEW', 'ELEVATION_VIEW', 'USERDEFINED', 'NOTDEFINED']
|
self.target_views = ['GRAPH_VIEW', 'SKETCH_VIEW', 'MODEL_VIEW', 'PLAN_VIEW', 'REFLECTED_PLAN_VIEW',
|
||||||
|
'SECTION_VIEW', 'ELEVATION_VIEW', 'USERDEFINED', 'NOTDEFINED']
|
||||||
self.should_export_all_materials_as_styled_items = False
|
self.should_export_all_materials_as_styled_items = False
|
||||||
self.should_use_presentation_style_assignment = False
|
self.should_use_presentation_style_assignment = False
|
||||||
# TODO make this configurable via UI
|
# TODO make this configurable via UI
|
||||||
|
|||||||
Reference in New Issue
Block a user