mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-05 23:41:44 +00:00
Revert "Fix #6392: when duplicating a window/door/etc, the associated IfcOpenElement duplicates as well."
This reverts commit a2a5780d59.
This commit is contained in:
@@ -151,29 +151,11 @@ class FilledOpeningGenerator:
|
||||
existing_opening_occurrence, "Model", "Body", "MODEL_VIEW"
|
||||
)
|
||||
assert representation
|
||||
|
||||
# Check if mapped representation - PRESERVE the mapping structure
|
||||
if (
|
||||
representation.RepresentationType == "MappedRepresentation"
|
||||
and len(representation.Items) == 1
|
||||
and representation.Items[0].is_a("IfcMappedItem")
|
||||
):
|
||||
# Store the existing RepresentationMap to reuse it
|
||||
existing_mapping_source = representation.Items[0].MappingSource
|
||||
reuse_mapped_representation = True
|
||||
else:
|
||||
representation = ifcopenshell.util.representation.resolve_representation(representation)
|
||||
|
||||
if not reuse_mapped_representation:
|
||||
# Check for library template before generating from filling
|
||||
template_rep = self.get_opening_template_from_type(filling)
|
||||
|
||||
if template_rep:
|
||||
representation = template_rep
|
||||
else:
|
||||
representation = self.generate_opening_from_filling(
|
||||
filling, filling_obj, opening_thickness_si=opening_thickness_si
|
||||
)
|
||||
representation = ifcopenshell.util.representation.resolve_representation(representation)
|
||||
else:
|
||||
representation = self.generate_opening_from_filling(
|
||||
filling, filling_obj, opening_thickness_si=opening_thickness_si
|
||||
)
|
||||
|
||||
# Create mapped representation
|
||||
if reuse_mapped_representation:
|
||||
@@ -247,109 +229,38 @@ class FilledOpeningGenerator:
|
||||
voided_element = opening.VoidsElements[0].RelatingBuildingElement
|
||||
|
||||
opening_rep = ifcopenshell.util.representation.get_representation(opening, "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
# ALWAYS preserve the existing opening representation (Tessellation, SweptSolid, etc.)
|
||||
preserved_representation = None
|
||||
if opening_rep:
|
||||
if (
|
||||
opening_rep.RepresentationType == "MappedRepresentation"
|
||||
and len(opening_rep.Items) == 1
|
||||
and opening_rep.Items[0].is_a("IfcMappedItem")
|
||||
):
|
||||
# For mapped representations, copy the underlying representation
|
||||
preserved_representation = ifcopenshell.util.element.copy_deep(
|
||||
tool.Ifc.get(),
|
||||
opening_rep.Items[0].MappingSource.MappedRepresentation,
|
||||
exclude=["IfcGeometricRepresentationContext"],
|
||||
)
|
||||
else:
|
||||
# For direct representations (non-mapped), copy them too
|
||||
preserved_representation = ifcopenshell.util.element.copy_deep(
|
||||
tool.Ifc.get(), opening_rep, exclude=["IfcGeometricRepresentationContext"]
|
||||
)
|
||||
|
||||
ifcopenshell.api.geometry.unassign_representation(tool.Ifc.get(), product=opening, representation=opening_rep)
|
||||
ifcopenshell.api.geometry.remove_representation(tool.Ifc.get(), representation=opening_rep)
|
||||
|
||||
existing_opening_occurrence = self.get_existing_opening_occurrence_if_any(filling)
|
||||
|
||||
# Priority order for choosing representation:
|
||||
# 1. Existing occurrence with MappedRepresentation (preserve mapping!)
|
||||
# 2. Library template with Tessellation
|
||||
# 3. Preserved representation from old opening (maintain user's work)
|
||||
# 4. Generate from filling (last resort)
|
||||
|
||||
representation_to_use = None
|
||||
reuse_mapped_representation = False
|
||||
existing_mapping_source = None
|
||||
|
||||
if existing_opening_occurrence:
|
||||
representation = ifcopenshell.util.representation.get_representation(
|
||||
existing_opening_occurrence, "Model", "Body", "MODEL_VIEW"
|
||||
)
|
||||
|
||||
if (
|
||||
representation
|
||||
and representation.RepresentationType == "MappedRepresentation"
|
||||
and len(representation.Items) == 1
|
||||
and representation.Items[0].is_a("IfcMappedItem")
|
||||
):
|
||||
# PRESERVE the mapped structure - reuse the same RepresentationMap
|
||||
existing_mapping_source = representation.Items[0].MappingSource
|
||||
reuse_mapped_representation = True
|
||||
else:
|
||||
representation_to_use = ifcopenshell.util.representation.resolve_representation(representation)
|
||||
|
||||
if not representation_to_use and not reuse_mapped_representation:
|
||||
template_rep = self.get_opening_template_from_type(filling)
|
||||
if template_rep and template_rep.RepresentationType == "Tessellation":
|
||||
representation_to_use = template_rep
|
||||
|
||||
if not representation_to_use and not reuse_mapped_representation and preserved_representation:
|
||||
representation_to_use = preserved_representation
|
||||
|
||||
if not representation_to_use and not reuse_mapped_representation:
|
||||
representation = ifcopenshell.util.representation.resolve_representation(representation)
|
||||
mapped_representation = ifcopenshell.api.geometry.map_representation(
|
||||
tool.Ifc.get(), representation=representation
|
||||
)
|
||||
ifcopenshell.api.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(element=opening)
|
||||
tool.Blender.remove_data_blocks([opening_obj], remove_unused_data=True)
|
||||
|
||||
filling_obj = tool.Ifc.get_object(filling)
|
||||
representation_to_use = self.generate_opening_from_filling(filling, filling_obj)
|
||||
|
||||
# Create the mapped representation
|
||||
if reuse_mapped_representation:
|
||||
# Reuse existing RepresentationMap - don't create a new one!
|
||||
context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
new_mapped_item = tool.Ifc.get().create_entity(
|
||||
"IfcMappedItem",
|
||||
MappingSource=existing_mapping_source,
|
||||
MappingTarget=tool.Ifc.get().create_entity(
|
||||
"IfcCartesianTransformationOperator3D",
|
||||
Axis1=tool.Ifc.get().create_entity("IfcDirection", DirectionRatios=(1.0, 0.0, 0.0)),
|
||||
Axis2=tool.Ifc.get().create_entity("IfcDirection", DirectionRatios=(0.0, 1.0, 0.0)),
|
||||
LocalOrigin=tool.Ifc.get().create_entity("IfcCartesianPoint", Coordinates=(0.0, 0.0, 0.0)),
|
||||
Scale=1.0,
|
||||
Axis3=tool.Ifc.get().create_entity("IfcDirection", DirectionRatios=(0.0, 0.0, 1.0)),
|
||||
),
|
||||
)
|
||||
mapped_representation = tool.Ifc.get().create_entity(
|
||||
"IfcShapeRepresentation",
|
||||
ContextOfItems=context,
|
||||
RepresentationIdentifier="Body",
|
||||
RepresentationType="MappedRepresentation",
|
||||
Items=[new_mapped_item],
|
||||
)
|
||||
else:
|
||||
representation = self.generate_opening_from_filling(filling, filling_obj)
|
||||
mapped_representation = ifcopenshell.api.geometry.map_representation(
|
||||
tool.Ifc.get(), representation=representation_to_use
|
||||
tool.Ifc.get(), representation=representation
|
||||
)
|
||||
ifcopenshell.api.geometry.assign_representation(
|
||||
tool.Ifc.get(), product=opening, representation=mapped_representation
|
||||
)
|
||||
|
||||
ifcopenshell.api.geometry.assign_representation(
|
||||
tool.Ifc.get(), product=opening, representation=mapped_representation
|
||||
)
|
||||
|
||||
# update voided object representation...
|
||||
# update voided object representation or all it's parts if it's an aggregate
|
||||
voided_elements = ifcopenshell.util.element.get_parts(voided_element) or [voided_element]
|
||||
for voided_element in voided_elements:
|
||||
voided_obj = tool.Ifc.get_object(voided_element)
|
||||
@@ -363,36 +274,6 @@ class FilledOpeningGenerator:
|
||||
representation=representation,
|
||||
)
|
||||
|
||||
def get_opening_template_from_type(
|
||||
self, filling: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""
|
||||
Check if the filling's type has a stored opening template from library import.
|
||||
"""
|
||||
element_type = ifcopenshell.util.element.get_type(filling)
|
||||
|
||||
if not element_type:
|
||||
return None
|
||||
|
||||
desc = element_type.Description
|
||||
|
||||
if not desc or "||BonsaiOpeningTemplate:" not in desc:
|
||||
return None
|
||||
|
||||
# Extract template ID
|
||||
marker = desc.split("||BonsaiOpeningTemplate:")[-1]
|
||||
template_id = int(marker.split("||")[0])
|
||||
|
||||
try:
|
||||
template_rep = tool.Ifc.get().by_id(template_id)
|
||||
# Make a copy so we don't reuse the same representation instance
|
||||
copied = ifcopenshell.util.element.copy_deep(
|
||||
tool.Ifc.get(), template_rep, exclude=["IfcGeometricRepresentationContext"]
|
||||
)
|
||||
return copied
|
||||
except:
|
||||
return None
|
||||
|
||||
def generate_opening_from_filling(
|
||||
self,
|
||||
filling: ifcopenshell.entity_instance,
|
||||
|
||||
@@ -618,8 +618,6 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not element:
|
||||
return {"FINISHED"}
|
||||
if element.is_a("IfcTypeProduct"):
|
||||
# Store opening template from library if it exists
|
||||
self.store_opening_template_from_library(element, library_file)
|
||||
self.import_type_from_ifc(element, context)
|
||||
elif element.is_a("IfcProduct"):
|
||||
# NOTE: Non-types are not exposed in UI directly
|
||||
@@ -720,53 +718,6 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if element.is_a("IfcSurfaceStyle") and not tool.Ifc.get_object_by_identifier(element.id()):
|
||||
ifc_importer.create_style(element)
|
||||
|
||||
def store_opening_template_from_library(
|
||||
self, element: ifcopenshell.entity_instance, library_file: ifcopenshell.file
|
||||
) -> None:
|
||||
"""
|
||||
Find an opening representation in the library and copy it to the current file
|
||||
as a template. Store the template ID on the type for later retrieval.
|
||||
"""
|
||||
try:
|
||||
library_element = library_file.by_guid(element.GlobalId)
|
||||
except:
|
||||
return
|
||||
|
||||
# Find occurrences with openings in the library
|
||||
library_occurrences = ifcopenshell.util.element.get_types(library_element)
|
||||
|
||||
for occurrence in library_occurrences:
|
||||
if not getattr(occurrence, "FillsVoids", None):
|
||||
continue
|
||||
|
||||
library_opening = occurrence.FillsVoids[0].RelatingOpeningElement
|
||||
library_opening_rep = ifcopenshell.util.representation.get_representation(
|
||||
library_opening, "Model", "Body", "MODEL_VIEW"
|
||||
)
|
||||
|
||||
if not library_opening_rep:
|
||||
continue
|
||||
|
||||
# Check if mapped representation
|
||||
if (
|
||||
library_opening_rep.RepresentationType == "MappedRepresentation"
|
||||
and len(library_opening_rep.Items) == 1
|
||||
and library_opening_rep.Items[0].is_a("IfcMappedItem")
|
||||
):
|
||||
|
||||
mapped_rep = library_opening_rep.Items[0].MappingSource.MappedRepresentation
|
||||
|
||||
# Store ALL representation types (Tessellation, SweptSolid, etc.)
|
||||
template_rep = ifcopenshell.util.element.copy_deep(
|
||||
self.file, mapped_rep, exclude=["IfcGeometricRepresentationContext"]
|
||||
)
|
||||
|
||||
# Store reference in type's Description
|
||||
current_desc = element.Description or ""
|
||||
element.Description = f"{current_desc}||BonsaiOpeningTemplate:{template_rep.id()}"
|
||||
return
|
||||
break
|
||||
|
||||
|
||||
class EditProjectLibrary(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_project_library"
|
||||
|
||||
@@ -93,38 +93,16 @@ class Root(bonsai.core.tool.Root):
|
||||
elif dest.is_a("IfcTypeProduct"):
|
||||
if not source.RepresentationMaps:
|
||||
return copied_entities
|
||||
|
||||
# Copy representation maps while preserving mapped representation structures
|
||||
new_maps = []
|
||||
for i, rep_map in enumerate(source.RepresentationMaps):
|
||||
source_rep = rep_map.MappedRepresentation
|
||||
|
||||
# Copy the map itself
|
||||
new_map = ifcopenshell.util.element.copy(tool.Ifc.get(), rep_map)
|
||||
|
||||
# Handle the mapped representation - preserve mapping structure if present
|
||||
if (
|
||||
source_rep.RepresentationType == "MappedRepresentation"
|
||||
and len(source_rep.Items) == 1
|
||||
and source_rep.Items[0].is_a("IfcMappedItem")
|
||||
):
|
||||
# This is a mapped representation - preserve the structure
|
||||
new_rep = ifcopenshell.util.element.copy(tool.Ifc.get(), source_rep)
|
||||
new_rep.Items = [ifcopenshell.util.element.copy(tool.Ifc.get(), item) for item in source_rep.Items]
|
||||
new_map.MappedRepresentation = new_rep
|
||||
else:
|
||||
# Not a mapped representation - use copy_deep as before
|
||||
new_map.MappedRepresentation = ifcopenshell.util.element.copy_deep(
|
||||
tool.Ifc.get(),
|
||||
source_rep,
|
||||
exclude=["IfcGeometricRepresentationContext"],
|
||||
exclude_callback=exclude_callback,
|
||||
copied_entities=copied_entities,
|
||||
)
|
||||
|
||||
new_maps.append(new_map)
|
||||
|
||||
dest.RepresentationMaps = new_maps
|
||||
dest.RepresentationMaps = [
|
||||
ifcopenshell.util.element.copy_deep(
|
||||
tool.Ifc.get(),
|
||||
m,
|
||||
exclude=["IfcGeometricRepresentationContext"],
|
||||
exclude_callback=exclude_callback,
|
||||
copied_entities=copied_entities,
|
||||
)
|
||||
for m in source.RepresentationMaps
|
||||
]
|
||||
return copied_entities
|
||||
|
||||
@classmethod
|
||||
|
||||
Reference in New Issue
Block a user