Files
IfcOpenShell/src/blenderbim/blenderbim/core/geometry.py
T

191 lines
8.0 KiB
Python

# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import blenderbim.core.style
def edit_object_placement(ifc, geometry, surveyor, obj=None):
element = ifc.get_entity(obj)
if not element:
return
geometry.clear_cache(element)
geometry.clear_scale(obj)
ifc.run("geometry.edit_object_placement", product=element, matrix=surveyor.get_absolute_matrix(obj))
geometry.record_object_position(obj)
def add_representation(
ifc, geometry, style, surveyor, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
):
element = ifc.get_entity(obj)
if not element:
return
edit_object_placement(ifc, geometry, surveyor, obj=obj)
data = geometry.get_object_data(obj)
if not data and ifc_representation_class != "IfcTextLiteral":
return
representation = ifc.run(
"geometry.add_representation",
context=context,
blender_object=obj,
geometry=data,
coordinate_offset=geometry.get_cartesian_point_coordinate_offset(obj),
total_items=geometry.get_total_representation_items(obj),
should_force_faceted_brep=geometry.should_force_faceted_brep(),
should_force_triangulation=geometry.should_force_triangulation(),
should_generate_uvs=geometry.should_generate_uvs(obj),
ifc_representation_class=ifc_representation_class,
profile_set_usage=profile_set_usage,
)
if geometry.is_body_representation(representation):
[geometry.run_style_add_style(obj=mat) for mat in geometry.get_object_materials_without_styles(obj)]
ifc.run(
"style.assign_representation_styles",
shape_representation=representation,
styles=geometry.get_styles(obj),
should_use_presentation_style_assignment=geometry.should_use_presentation_style_assignment(),
)
geometry.record_object_materials(obj)
ifc.run("geometry.assign_representation", product=element, representation=representation)
if data:
data = geometry.duplicate_object_data(obj)
geometry.change_object_data(obj, data, is_global=True)
name = geometry.get_representation_name(representation)
geometry.rename_object(data, name)
geometry.link(representation, data)
return representation
def switch_representation(
ifc,
geometry,
obj=None,
representation=None,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
apply_openings=True,
):
"""Function can switch to representation that wasn't yet assigned to that object. See #2766.
`should_sync_changes_first` - sync ifc representation with current state of `obj.data`;
`should_reload` - reload `obj.data` from ifc representation;
`is_global` - replace mesh data for all users of `obj.data`, not just `obj`;
"""
if should_sync_changes_first and geometry.is_edited(obj) and not geometry.is_box_representation(representation):
representation_id = geometry.get_representation_id(representation)
geometry.run_geometry_update_representation(obj=obj)
if not geometry.does_representation_id_exist(representation_id):
return
entity = ifc.get_entity(obj)
current_obj_data = geometry.get_object_data(obj)
has_openings = apply_openings and getattr(entity, "HasOpenings", None)
if has_openings:
# if it has openings make sure to switch to element's mapped representation
representation = geometry.unresolve_type_representation(representation, entity)
else:
# doesn't resolve mapped representations in case if it's going to have openings
# otherwise we would also add openings to the type and other occurences mesh data
representation = geometry.resolve_mapped_representation(representation)
old_repr_data = geometry.get_representation_data(representation)
if should_reload or not old_repr_data:
new_repr_data = geometry.import_representation(obj, representation, apply_openings=apply_openings)
geometry.rename_object(new_repr_data, geometry.get_representation_name(representation))
geometry.link(representation, new_repr_data)
else:
new_repr_data = old_repr_data
geometry.change_object_data(obj, new_repr_data, is_global=is_global and not has_openings)
geometry.record_object_materials(obj)
# we assume that all the occurences and the type have the same representation context active
# so geometry.delete_data cannot remove the data that's still used by some other object
if should_reload and old_repr_data:
# if current object was using some temporary mesh (like during profile edit mode) instead of `old_repr_data`
# then `change_object_data` won't switch the mesh for all the occurences and we need to do it explicitly
if current_obj_data != old_repr_data and geometry.has_data_users(old_repr_data):
geometry.replace_object_data_globally(old_repr_data, new_repr_data)
geometry.delete_data(old_repr_data)
geometry.clear_modifiers(obj)
geometry.clear_cache(entity)
def get_representation_ifc_parameters(geometry, obj=None, should_sync_changes_first=False):
geometry.import_representation_parameters(geometry.get_object_data(obj))
def remove_representation(ifc, geometry, obj=None, representation=None):
"""Consider changing obj representation before using the function,
otherwise it will replace object with empty."""
element = ifc.get_entity(obj)
element_type = geometry.get_element_type(element)
data = None
if element_type and (geometry.is_mapped_representation(representation) or geometry.is_type_product(element)):
representation = geometry.resolve_mapped_representation(representation)
data = geometry.get_representation_data(representation)
if data and geometry.has_data_users(data):
for element in geometry.get_elements_of_type(element_type):
obj = ifc.get_object(element)
if obj:
obj = geometry.replace_object_with_empty(obj)
obj = ifc.get_object(element_type)
if obj:
obj = geometry.replace_object_with_empty(obj)
ifc.run("geometry.unassign_representation", product=element_type, representation=representation)
ifc.run("geometry.remove_representation", representation=representation)
else:
data = geometry.get_representation_data(representation)
if data and geometry.has_data_users(data):
geometry.replace_object_with_empty(obj)
ifc.run("geometry.unassign_representation", product=element, representation=representation)
ifc.run("geometry.remove_representation", representation=representation)
if data:
geometry.delete_data(data)
def purge_unused_representations(ifc, geometry):
purged_representations = 0
for representation in geometry.get_model_representations():
if ifc.get().get_total_inverses(representation) == 0:
ifc.run("geometry.remove_representation", representation=representation)
purged_representations += 1
return purged_representations
def select_connection(geometry, connection=None):
geometry.select_connection(connection)
def remove_connection(geometry, connection=None):
geometry.remove_connection(connection)