mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-17 14:02:27 +00:00
Fix #1806. Changing filling types now also updates the void representation.
This commit is contained in:
@@ -93,3 +93,9 @@ def load_post(*args):
|
|||||||
"BlenderBIM.DumbProfile.RegenerateFromType",
|
"BlenderBIM.DumbProfile.RegenerateFromType",
|
||||||
profile.DumbProfileRegenerator().regenerate_from_type,
|
profile.DumbProfileRegenerator().regenerate_from_type,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ifcopenshell.api.add_post_listener(
|
||||||
|
"type.assign_type",
|
||||||
|
"BlenderBIM.Opening.RegenerateFromType",
|
||||||
|
opening.FilledOpeningGenerator().regenerate_from_type,
|
||||||
|
)
|
||||||
|
|||||||
@@ -46,24 +46,28 @@ class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
filling_obj: bpy.props.StringProperty()
|
filling_obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
props = context.scene.BIMModelProperties
|
FilledOpeningGenerator().generate(bpy.data.objects.get(self.filling_obj), bpy.data.objects.get(self.voided_obj))
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class FilledOpeningGenerator:
|
||||||
|
def generate(self, filling_obj, voided_obj):
|
||||||
|
props = bpy.context.scene.BIMModelProperties
|
||||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
|
|
||||||
voided_obj = bpy.data.objects.get(self.voided_obj)
|
|
||||||
filling_obj = bpy.data.objects.get(self.filling_obj)
|
|
||||||
filling = tool.Ifc.get_entity(filling_obj)
|
filling = tool.Ifc.get_entity(filling_obj)
|
||||||
|
|
||||||
if not voided_obj or not filling_obj:
|
if not voided_obj or not filling_obj:
|
||||||
return {"FINISHED"}
|
return
|
||||||
|
|
||||||
element = tool.Ifc.get_entity(voided_obj)
|
element = tool.Ifc.get_entity(voided_obj)
|
||||||
target = context.scene.cursor.location
|
target = bpy.context.scene.cursor.location
|
||||||
raycast = voided_obj.closest_point_on_mesh(voided_obj.matrix_world.inverted() @ target, distance=0.01)
|
raycast = voided_obj.closest_point_on_mesh(voided_obj.matrix_world.inverted() @ target, distance=0.01)
|
||||||
if not raycast[0]:
|
if not raycast[0]:
|
||||||
target = filling_obj.matrix_world.col[3].to_3d().copy()
|
target = filling_obj.matrix_world.col[3].to_3d().copy()
|
||||||
raycast = voided_obj.closest_point_on_mesh(voided_obj.matrix_world.inverted() @ target, distance=0.5)
|
raycast = voided_obj.closest_point_on_mesh(voided_obj.matrix_world.inverted() @ target, distance=0.5)
|
||||||
if not raycast[0]:
|
if not raycast[0]:
|
||||||
return {"FINISHED"}
|
return
|
||||||
|
|
||||||
# In this prototype, we assume openings are only added to axis-based elements
|
# In this prototype, we assume openings are only added to axis-based elements
|
||||||
layers = tool.Model.get_material_layer_parameters(element)
|
layers = tool.Model.get_material_layer_parameters(element)
|
||||||
@@ -80,41 +84,23 @@ class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
filling_obj.matrix_world = new_matrix
|
filling_obj.matrix_world = new_matrix
|
||||||
bpy.context.view_layer.update()
|
bpy.context.view_layer.update()
|
||||||
|
|
||||||
existing_opening_occurrence = self.get_existing_opening_occurrence_if_any(filling)
|
|
||||||
|
|
||||||
if existing_opening_occurrence:
|
|
||||||
existing_opening_occurrence_obj = tool.Ifc.get_object(existing_opening_occurrence)
|
|
||||||
if existing_opening_occurrence_obj:
|
|
||||||
opening_obj = bpy.data.objects.new("Opening", existing_opening_occurrence_obj.data)
|
|
||||||
opening_obj.data = existing_opening_occurrence_obj.data
|
|
||||||
else:
|
|
||||||
opening_obj = self.create_object_using_element_representation(existing_opening_occurrence)
|
|
||||||
opening_obj.matrix_world = filling_obj.matrix_world
|
|
||||||
else:
|
|
||||||
opening_obj = self.generate_opening_from_filling(filling, filling_obj, voided_obj)
|
|
||||||
|
|
||||||
# Still prototyping, for now duplicating code from bpy.ops.bim.add_opening
|
|
||||||
if tool.Ifc.is_moved(voided_obj):
|
if tool.Ifc.is_moved(voided_obj):
|
||||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=voided_obj)
|
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=voided_obj)
|
||||||
|
|
||||||
# Ensure we check this prior to adding the class
|
existing_opening_occurrence = self.get_existing_opening_occurrence_if_any(filling)
|
||||||
has_visible_openings = self.has_visible_openings(element)
|
|
||||||
|
|
||||||
body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body")
|
|
||||||
opening = blenderbim.core.root.assign_class(
|
|
||||||
tool.Ifc,
|
|
||||||
tool.Collector,
|
|
||||||
tool.Root,
|
|
||||||
obj=opening_obj,
|
|
||||||
ifc_class="IfcOpeningElement",
|
|
||||||
should_add_representation=not bool(existing_opening_occurrence),
|
|
||||||
context=body_context,
|
|
||||||
)
|
|
||||||
|
|
||||||
if existing_opening_occurrence:
|
if existing_opening_occurrence:
|
||||||
|
opening = ifcopenshell.api.run(
|
||||||
|
"root.create_entity",
|
||||||
|
tool.Ifc.get(),
|
||||||
|
ifc_class="IfcOpeningElement",
|
||||||
|
predefined_type="OPENING",
|
||||||
|
name="Opening",
|
||||||
|
)
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"geometry.edit_object_placement", tool.Ifc.get(), product=opening, matrix=filling_obj.matrix_world
|
"geometry.edit_object_placement", tool.Ifc.get(), product=opening, matrix=filling_obj.matrix_world
|
||||||
)
|
)
|
||||||
|
|
||||||
representation = ifcopenshell.util.representation.get_representation(
|
representation = ifcopenshell.util.representation.get_representation(
|
||||||
existing_opening_occurrence, "Model", "Body", "MODEL_VIEW"
|
existing_opening_occurrence, "Model", "Body", "MODEL_VIEW"
|
||||||
)
|
)
|
||||||
@@ -126,6 +112,17 @@ class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
"geometry.assign_representation", tool.Ifc.get(), product=opening, representation=mapped_representation
|
"geometry.assign_representation", tool.Ifc.get(), product=opening, representation=mapped_representation
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
has_visible_openings = self.has_visible_openings(element)
|
||||||
|
opening_obj = self.generate_opening_from_filling(filling, filling_obj, voided_obj)
|
||||||
|
opening = blenderbim.core.root.assign_class(
|
||||||
|
tool.Ifc,
|
||||||
|
tool.Collector,
|
||||||
|
tool.Root,
|
||||||
|
obj=opening_obj,
|
||||||
|
ifc_class="IfcOpeningElement",
|
||||||
|
should_add_representation=True,
|
||||||
|
context=ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body"),
|
||||||
|
)
|
||||||
representation = ifcopenshell.util.representation.get_representation(opening, "Model", "Body", "MODEL_VIEW")
|
representation = ifcopenshell.util.representation.get_representation(opening, "Model", "Body", "MODEL_VIEW")
|
||||||
mapped_representation = ifcopenshell.api.run(
|
mapped_representation = ifcopenshell.api.run(
|
||||||
"geometry.map_representation", tool.Ifc.get(), representation=representation
|
"geometry.map_representation", tool.Ifc.get(), representation=representation
|
||||||
@@ -136,8 +133,12 @@ class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"geometry.assign_representation", tool.Ifc.get(), product=opening, representation=mapped_representation
|
"geometry.assign_representation", tool.Ifc.get(), product=opening, representation=mapped_representation
|
||||||
)
|
)
|
||||||
|
if not has_visible_openings:
|
||||||
|
tool.Ifc.unlink(obj=opening_obj)
|
||||||
|
bpy.data.objects.remove(opening_obj)
|
||||||
|
|
||||||
ifcopenshell.api.run("void.add_opening", tool.Ifc.get(), opening=opening, element=element)
|
ifcopenshell.api.run("void.add_opening", tool.Ifc.get(), opening=opening, element=element)
|
||||||
|
ifcopenshell.api.run("void.add_filling", tool.Ifc.get(), opening=opening, element=filling)
|
||||||
|
|
||||||
representation = tool.Ifc.get().by_id(voided_obj.data.BIMMeshProperties.ifc_definition_id)
|
representation = tool.Ifc.get().by_id(voided_obj.data.BIMMeshProperties.ifc_definition_id)
|
||||||
blenderbim.core.geometry.switch_representation(
|
blenderbim.core.geometry.switch_representation(
|
||||||
@@ -150,14 +151,78 @@ class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
should_sync_changes_first=False,
|
should_sync_changes_first=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
bpy.ops.bim.add_filling(opening=opening_obj.name, obj=filling_obj.name)
|
return {"FINISHED"}
|
||||||
|
|
||||||
if not has_visible_openings:
|
def regenerate_from_type(self, usecase_path, ifc_file, settings):
|
||||||
# It's a little strange how we create an object just to delete it.
|
filling = settings["related_object"]
|
||||||
# In the future this could be streamlined.
|
if not filling.FillsVoids:
|
||||||
|
return
|
||||||
|
|
||||||
|
opening = filling.FillsVoids[0].RelatingOpeningElement
|
||||||
|
voided_obj = tool.Ifc.get_object(opening.VoidsElements[0].RelatingBuildingElement)
|
||||||
|
|
||||||
|
opening_rep = ifcopenshell.util.representation.get_representation(opening, "Model", "Body", "MODEL_VIEW")
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"geometry.unassign_representation", tool.Ifc.get(), product=opening, representation=opening_rep
|
||||||
|
)
|
||||||
|
|
||||||
|
existing_opening_occurrence = self.get_existing_opening_occurrence_if_any(filling)
|
||||||
|
|
||||||
|
if existing_opening_occurrence:
|
||||||
|
representation = ifcopenshell.util.representation.get_representation(
|
||||||
|
existing_opening_occurrence, "Model", "Body", "MODEL_VIEW"
|
||||||
|
)
|
||||||
|
representation = ifcopenshell.util.representation.resolve_representation(representation)
|
||||||
|
mapped_representation = ifcopenshell.api.run(
|
||||||
|
"geometry.map_representation", tool.Ifc.get(), representation=representation
|
||||||
|
)
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"geometry.assign_representation", tool.Ifc.get(), product=opening, representation=mapped_representation
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
opening_obj = tool.Ifc.get_object(opening)
|
||||||
|
if opening_obj:
|
||||||
|
tool.Ifc.unlink(obj=opening_obj)
|
||||||
|
bpy.data.objects.remove(opening_obj)
|
||||||
|
|
||||||
|
filling_obj = tool.Ifc.get_object(filling)
|
||||||
|
opening_obj = self.generate_opening_from_filling(filling, filling_obj, voided_obj)
|
||||||
|
tool.Ifc.link(opening, opening_obj)
|
||||||
|
|
||||||
|
representation = ifcopenshell.api.run(
|
||||||
|
"geometry.add_representation",
|
||||||
|
tool.Ifc.get(),
|
||||||
|
context=ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body"),
|
||||||
|
blender_object=opening_obj,
|
||||||
|
geometry=opening_obj.data,
|
||||||
|
coordinate_offset=tool.Geometry.get_cartesian_point_coordinate_offset(opening_obj),
|
||||||
|
total_items=tool.Geometry.get_total_representation_items(opening_obj),
|
||||||
|
should_force_faceted_brep=tool.Geometry.should_force_faceted_brep(),
|
||||||
|
should_force_triangulation=tool.Geometry.should_force_triangulation(),
|
||||||
|
should_generate_uvs=tool.Geometry.should_generate_uvs(opening_obj),
|
||||||
|
ifc_representation_class=None,
|
||||||
|
profile_set_usage=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
mapped_representation = ifcopenshell.api.run(
|
||||||
|
"geometry.map_representation", tool.Ifc.get(), representation=representation
|
||||||
|
)
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"geometry.assign_representation", tool.Ifc.get(), product=opening, representation=mapped_representation
|
||||||
|
)
|
||||||
tool.Ifc.unlink(obj=opening_obj)
|
tool.Ifc.unlink(obj=opening_obj)
|
||||||
bpy.data.objects.remove(opening_obj)
|
bpy.data.objects.remove(opening_obj)
|
||||||
return {"FINISHED"}
|
|
||||||
|
representation = tool.Ifc.get().by_id(voided_obj.data.BIMMeshProperties.ifc_definition_id)
|
||||||
|
blenderbim.core.geometry.switch_representation(
|
||||||
|
tool.Ifc,
|
||||||
|
tool.Geometry,
|
||||||
|
obj=voided_obj,
|
||||||
|
representation=representation,
|
||||||
|
should_reload=True,
|
||||||
|
is_global=True,
|
||||||
|
should_sync_changes_first=False,
|
||||||
|
)
|
||||||
|
|
||||||
def generate_opening_from_filling(self, filling, filling_obj, voided_obj):
|
def generate_opening_from_filling(self, filling, filling_obj, voided_obj):
|
||||||
profile = ifcopenshell.util.representation.get_representation(filling, "Model", "Profile", "ELEVATION_VIEW")
|
profile = ifcopenshell.util.representation.get_representation(filling, "Model", "Profile", "ELEVATION_VIEW")
|
||||||
@@ -168,9 +233,7 @@ class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
def has_visible_openings(self, element):
|
def has_visible_openings(self, element):
|
||||||
for opening in [r.RelatedOpeningElement for r in element.HasOpenings]:
|
for opening in [r.RelatedOpeningElement for r in element.HasOpenings]:
|
||||||
if tool.Ifc.get_object(opening):
|
if tool.Ifc.get_object(opening):
|
||||||
print("OPENINGS ARE VISIBLE")
|
|
||||||
return True
|
return True
|
||||||
print("OPENINGS ARE NOTOOOTT VISIBLE")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_existing_opening_occurrence_if_any(self, filling):
|
def get_existing_opening_occurrence_if_any(self, filling):
|
||||||
@@ -181,31 +244,6 @@ class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
if filling_occurrence != filling and filling_occurrence.FillsVoids:
|
if filling_occurrence != filling and filling_occurrence.FillsVoids:
|
||||||
return filling_occurrence.FillsVoids[0].RelatingOpeningElement
|
return filling_occurrence.FillsVoids[0].RelatingOpeningElement
|
||||||
|
|
||||||
def create_object_using_element_representation(self, opening):
|
|
||||||
settings = ifcopenshell.geom.settings()
|
|
||||||
|
|
||||||
shape = ifcopenshell.geom.create_shape(settings, opening)
|
|
||||||
verts = shape.geometry.verts
|
|
||||||
faces = shape.geometry.faces
|
|
||||||
grouped_verts = [[verts[i], verts[i + 1], verts[i + 2]] for i in range(0, len(verts), 3)]
|
|
||||||
grouped_faces = [[faces[i], faces[i + 1], faces[i + 2]] for i in range(0, len(faces), 3)]
|
|
||||||
|
|
||||||
bm = bmesh.new()
|
|
||||||
bm.verts.index_update()
|
|
||||||
bm.faces.index_update()
|
|
||||||
new_verts = [bm.verts.new(v) for v in grouped_verts]
|
|
||||||
new_faces = [bm.faces.new((new_verts[f[0]], new_verts[f[1]], new_verts[f[2]])) for f in grouped_faces]
|
|
||||||
|
|
||||||
bm.verts.index_update()
|
|
||||||
bm.faces.index_update()
|
|
||||||
|
|
||||||
mesh = bpy.data.meshes.new(name="Opening")
|
|
||||||
bm.to_mesh(mesh)
|
|
||||||
bm.free()
|
|
||||||
|
|
||||||
obj = bpy.data.objects.new("Opening", mesh)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def generate_opening_from_filling_profile(self, filling_obj, voided_obj, profile):
|
def generate_opening_from_filling_profile(self, filling_obj, voided_obj, profile):
|
||||||
settings = ifcopenshell.geom.settings()
|
settings = ifcopenshell.geom.settings()
|
||||||
settings.set(settings.INCLUDE_CURVES, True)
|
settings.set(settings.INCLUDE_CURVES, True)
|
||||||
@@ -712,9 +750,7 @@ class EditOpenings(Operator, tool.Ifc.Operator):
|
|||||||
bpy.data.objects.remove(opening_obj)
|
bpy.data.objects.remove(opening_obj)
|
||||||
|
|
||||||
for obj in building_objs:
|
for obj in building_objs:
|
||||||
print('updating building obj', obj)
|
|
||||||
element = tool.Ifc.get_entity(obj)
|
element = tool.Ifc.get_entity(obj)
|
||||||
print('element is', element)
|
|
||||||
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||||
blenderbim.core.geometry.switch_representation(
|
blenderbim.core.geometry.switch_representation(
|
||||||
tool.Ifc,
|
tool.Ifc,
|
||||||
|
|||||||
Reference in New Issue
Block a user