Fix #4139. Bulk purge orphaned classes and move advanced operators to debug panel.

This commit is contained in:
Dion Moult
2024-01-15 23:16:29 +11:00
parent 39ee9cd8b1
commit 3037795199
3 changed files with 117 additions and 10 deletions
@@ -510,8 +510,116 @@ class PurgeUnusedElementsByClass(bpy.types.Operator, tool.Ifc.Operator):
"Will find all elements of class that have no inverse refernces and will remove them, use very carefully" "Will find all elements of class that have no inverse refernces and will remove them, use very carefully"
) )
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMDebugProperties props = context.scene.BIMDebugProperties
purged_elements = core.purge_unused_elements(tool.Ifc, tool.Debug, props.ifc_class_purge) if props.ifc_class_purge:
self.report({"INFO"}, f"{purged_elements} unused elements found and removed.") purged_elements = core.purge_unused_elements(tool.Ifc, tool.Debug, props.ifc_class_purge)
self.report({"INFO"}, f"{purged_elements} unused elements found and removed.")
tool.Ifc.get().write(self.filepath)
return
# A whitelisted class is a class that only contains simple data that
# has no meaning by itself (only has meaning when combined with other
# data). E.g. a colour by itself has no meaning so is whitelisted, but
# a material by itself may be part of your materials library.
# There are classes I don't know enough about to decide. Not whitelisting in case.
# IfcAlignmentParameterSegment
# IfcBoundaryCondition
# IfcStructuralConnectionCondition
# IfcStructuralLoad
whitelisted_classes = [
"IfcActorRole",
"IfcAddress",
"IfcApplication",
"IfcAppliedValue",
"IfcConnectionGeometry",
"IfcCoordinateReferenceSystem",
"IfcDerivedUnit",
"IfcDerivedUnitElement",
"IfcDimensionalExponents",
"IfcExternalReference",
"IfcGridAxis",
"IfcIrregularTimeSeriesValue",
"IfcLightDistributionData",
"IfcLightIntensityDistribution",
"IfcMeasureWithUnit",
"IfcMonetaryUnit",
"IfcNamedUnit",
"IfcObjectPlacement",
"IfcOrganization", # Should be referenced as part of an actor
"IfcOwnerHistory",
"IfcPerson", # Should be referenced as part of an actor
"IfcPersonAndOrganization", # Should be referenced as part of an actor
"IfcPhysicalQuantity",
"IfcPresentationItem",
"IfcProductDefinitionShape",
"IfcPropertyAbstraction",
"IfcRecurrencePattern",
"IfcReference",
"IfcRepresentation",
"IfcRepresentationContext",
"IfcRepresentationItem",
"IfcRepresentationMap",
"IfcPropertyDefinition", # A bit of a questionable one, and the odd one out from IfcRoot.
"IfcSchedulingTime",
"IfcShapeAspect",
"IfcTable",
"IfcTableColumn",
"IfcTableRow",
"IfcTextureCoordinateIndices",
"IfcTimePeriod",
"IfcTimeSeries",
"IfcTimeSeriesValue",
"IfcUnitAssignment",
"IfcVirtualGridIntersection",
]
total_purged = 0
while True:
total_batches = 0
print("*" * 100)
total_batch_purged = 0
for ifc_class in whitelisted_classes:
total_class_purged = 0
try:
elements = tool.Ifc.get().by_type(ifc_class)
except:
continue # Probably not in this schema?
to_purge = set()
for element in elements:
try:
if ifc_class == "IfcRepresentationItem" and element.is_a("IfcStyledItem") and element.Item:
continue
except:
to_purge.add(element.id()) # It's invalid, definitely purge it.
if tool.Ifc.get().get_total_inverses(element) == 0:
to_purge.add(element.id())
for element_id in to_purge:
try:
element = tool.Ifc.get().by_id(element_id)
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), element)
total_class_purged += 1
except:
continue
if total_class_purged > 0:
total_batch_purged += total_class_purged
print(f"Auto purged {total_class_purged} {ifc_class}")
if total_batch_purged > 0:
total_purged += total_batch_purged
print(f"Auto purged in batch: {total_batch_purged}")
total_batches += 1
if total_batch_purged == 0:
break
elif total_batches > 20:
print("Finished 20 batches. Manually stopping in case of infinite loop.")
self.report({"INFO"}, f"Auto purged {total_purged} orphaned elements")
tool.Ifc.get().write(self.filepath)
@@ -87,6 +87,13 @@ class BIM_PT_debug(Panel):
row.prop(props, "display_type", text="") row.prop(props, "display_type", text="")
row.operator("bim.override_display_type").display = context.scene.BIMDebugProperties.display_type row.operator("bim.override_display_type").display = context.scene.BIMDebugProperties.display_type
layout.operator("bim.purge_unused_representations")
row = layout.row(align=True)
row.prop(context.scene.BIMDebugProperties, "ifc_class_purge", text="")
row.operator("bim.purge_unused_elements_by_class", text="Purge Orphaned", icon="TRASH")
row.operator("bim.print_unused_elements_stats", text="", icon="INFO")
if context.active_object and context.active_object.data: if context.active_object and context.active_object.data:
mprops = context.active_object.data.BIMMeshProperties mprops = context.active_object.data.BIMMeshProperties
row = layout.row() row = layout.row()
@@ -399,11 +399,3 @@ class BIM_PT_purge(Panel):
layout = self.layout layout = self.layout
layout.operator("bim.purge_unused_profiles") layout.operator("bim.purge_unused_profiles")
layout.operator("bim.purge_unused_types") layout.operator("bim.purge_unused_types")
layout.operator("bim.purge_unused_representations")
layout.separator()
layout.label(text="Purge unused elements by class:")
row = layout.row(align=True)
row.prop(context.scene.BIMDebugProperties, "ifc_class_purge", text="")
row.operator("bim.purge_unused_elements_by_class", text="", icon="TRASH")
row.operator("bim.print_unused_elements_stats", text="", icon="INFO")