mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-17 22:11:36 +00:00
Fix #2274. You can now purge unused types.
This commit is contained in:
@@ -50,8 +50,9 @@ class LaunchTypeManager(bpy.types.Operator):
|
|||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
props = context.scene.BIMModelProperties
|
props = context.scene.BIMModelProperties
|
||||||
|
|
||||||
row = self.layout.row()
|
row = self.layout.row(align=True)
|
||||||
prop_with_search(row, props, "type_class", text="")
|
prop_with_search(row, props, "type_class", text="")
|
||||||
|
row.operator("bim.purge_unused_types", icon="TRASH", text="")
|
||||||
|
|
||||||
columns = self.layout.column_flow(columns=3)
|
columns = self.layout.column_flow(columns=3)
|
||||||
row = columns.row()
|
row = columns.row()
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ classes = (
|
|||||||
operator.DisableEditingType,
|
operator.DisableEditingType,
|
||||||
operator.DuplicateType,
|
operator.DuplicateType,
|
||||||
operator.EnableEditingType,
|
operator.EnableEditingType,
|
||||||
|
operator.PurgeUnusedTypes,
|
||||||
operator.RemoveType,
|
operator.RemoveType,
|
||||||
operator.SelectSimilarType,
|
operator.SelectSimilarType,
|
||||||
operator.SelectType,
|
operator.SelectType,
|
||||||
|
|||||||
@@ -378,3 +378,12 @@ class DuplicateType(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
new.Name += " Copy"
|
new.Name += " Copy"
|
||||||
bpy.ops.bim.load_type_thumbnails(ifc_class=new.is_a())
|
bpy.ops.bim.load_type_thumbnails(ifc_class=new.is_a())
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class PurgeUnusedTypes(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.purge_unused_types"
|
||||||
|
bl_label = "Purge Unused Types"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
core.purge_unused_types(tool.Ifc, tool.Type)
|
||||||
|
|||||||
@@ -89,6 +89,5 @@ def select_container(ifc, spatial, obj=None):
|
|||||||
|
|
||||||
def select_similar_container(ifc, spatial, obj=None):
|
def select_similar_container(ifc, spatial, obj=None):
|
||||||
element = ifc.get_entity(obj)
|
element = ifc.get_entity(obj)
|
||||||
if not element:
|
if element:
|
||||||
return
|
spatial.select_products(spatial.get_decomposed_elements(spatial.get_container(element)))
|
||||||
spatial.select_products(products=spatial.get_decomposed_elements(spatial.get_container(element)))
|
|
||||||
|
|||||||
@@ -642,14 +642,12 @@ class Spatial:
|
|||||||
def get_object_matrix(cls, obj): pass
|
def get_object_matrix(cls, obj): pass
|
||||||
def get_relative_object_matrix(cls, target_obj, relative_to_obj): pass
|
def get_relative_object_matrix(cls, target_obj, relative_to_obj): pass
|
||||||
def get_selected_product_types(cls): pass
|
def get_selected_product_types(cls): pass
|
||||||
def get_selected_products_and_types(cls): pass
|
|
||||||
def get_selected_products(cls): pass
|
|
||||||
def get_selected_products(cls): pass
|
def get_selected_products(cls): pass
|
||||||
def import_containers(cls, parent=None): pass
|
def import_containers(cls, parent=None): pass
|
||||||
def run_root_copy_class(cls, obj=None): pass
|
def run_root_copy_class(cls, obj=None): pass
|
||||||
def run_spatial_assign_container(cls, structure_obj=None, element_obj=None): pass
|
def run_spatial_assign_container(cls, structure_obj=None, element_obj=None): pass
|
||||||
def select_object(cls, obj): pass
|
def select_object(cls, obj): pass
|
||||||
def select_products(cls, products, unhide): pass
|
def select_products(cls, products, unhide=False): pass
|
||||||
def set_active_object(cls, obj): pass
|
def set_active_object(cls, obj): pass
|
||||||
def set_relative_object_matrix(cls, target_obj, relative_to_obj, matrix): pass
|
def set_relative_object_matrix(cls, target_obj, relative_to_obj, matrix): pass
|
||||||
def show_scene_objects(cls): pass
|
def show_scene_objects(cls): pass
|
||||||
@@ -728,10 +726,13 @@ class Type:
|
|||||||
def get_body_context(cls): pass
|
def get_body_context(cls): pass
|
||||||
def get_body_representation(cls, element): pass
|
def get_body_representation(cls, element): pass
|
||||||
def get_ifc_representation_class(cls, element): pass
|
def get_ifc_representation_class(cls, element): pass
|
||||||
|
def get_model_types(cls): pass
|
||||||
def get_object_data(cls, obj): pass
|
def get_object_data(cls, obj): pass
|
||||||
def get_profile_set_usage(cls, element): pass
|
def get_profile_set_usage(cls, element): pass
|
||||||
def get_representation_context(cls, representation): pass
|
def get_representation_context(cls, representation): pass
|
||||||
|
def get_type_occurrences(cls, element_type): pass
|
||||||
def has_material_usage(cls, element): pass
|
def has_material_usage(cls, element): pass
|
||||||
|
def remove_object(cls, obj): pass
|
||||||
def run_geometry_add_representation(cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None): pass
|
def run_geometry_add_representation(cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None): pass
|
||||||
def run_geometry_switch_representation(cls, obj=None, representation=None, should_reload=None, is_global=None): pass
|
def run_geometry_switch_representation(cls, obj=None, representation=None, should_reload=None, is_global=None): pass
|
||||||
|
|
||||||
|
|||||||
@@ -29,3 +29,13 @@ def assign_type(ifc, type_tool, element=None, type=None):
|
|||||||
if type_data:
|
if type_data:
|
||||||
type_tool.change_object_data(obj, type_data, is_global=False)
|
type_tool.change_object_data(obj, type_data, is_global=False)
|
||||||
type_tool.disable_editing(obj)
|
type_tool.disable_editing(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def purge_unused_types(ifc, type):
|
||||||
|
for element_type in type.get_model_types():
|
||||||
|
if not type.get_type_occurrences(element_type):
|
||||||
|
ifc.run("root.remove_product", product=element_type)
|
||||||
|
obj = ifc.get_object(element_type)
|
||||||
|
if obj:
|
||||||
|
ifc.unlink(obj=obj)
|
||||||
|
type.remove_object(obj)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# 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/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
import blenderbim.core.geometry
|
import blenderbim.core.geometry
|
||||||
@@ -59,6 +60,10 @@ class Type(blenderbim.core.tool.Type):
|
|||||||
elif material.is_a("IfcMaterialLayerSetUsage"):
|
elif material.is_a("IfcMaterialLayerSetUsage"):
|
||||||
return "IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids"
|
return "IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_model_types(cls):
|
||||||
|
return tool.Ifc.get().by_type("IfcElementType")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_object_data(cls, obj):
|
def get_object_data(cls, obj):
|
||||||
return obj.data
|
return obj.data
|
||||||
@@ -74,6 +79,10 @@ class Type(blenderbim.core.tool.Type):
|
|||||||
def get_representation_context(cls, representation):
|
def get_representation_context(cls, representation):
|
||||||
return representation.ContextOfItems
|
return representation.ContextOfItems
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_type_occurrences(cls, element_type):
|
||||||
|
return ifcopenshell.util.element.get_types(element_type)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def has_material_usage(cls, element):
|
def has_material_usage(cls, element):
|
||||||
material = ifcopenshell.util.element.get_material(element)
|
material = ifcopenshell.util.element.get_material(element)
|
||||||
@@ -81,6 +90,10 @@ class Type(blenderbim.core.tool.Type):
|
|||||||
return "Usage" in material.is_a()
|
return "Usage" in material.is_a()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remove_object(cls, obj):
|
||||||
|
bpy.data.objects.remove(obj)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run_geometry_add_representation(
|
def run_geometry_add_representation(
|
||||||
cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
|
cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
|
||||||
|
|||||||
@@ -1,6 +1,25 @@
|
|||||||
@type
|
@type
|
||||||
Feature: Type
|
Feature: Type
|
||||||
|
|
||||||
|
Scenario: Add type - adding via manual class assignment
|
||||||
|
Given an empty IFC project
|
||||||
|
And I add a cube
|
||||||
|
And the object "Cube" is selected
|
||||||
|
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||||
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||||
|
When I press "bim.assign_class"
|
||||||
|
Then nothing happens
|
||||||
|
|
||||||
|
Scenario: Add type - add from empty template
|
||||||
|
Given an empty IFC project
|
||||||
|
And I press "bim.launch_type_manager"
|
||||||
|
And I set "scene.BIMModelProperties.type_class" to "IfcWallType"
|
||||||
|
And I set "scene.BIMModelProperties.type_predefined_type" to "SOLIDWALL"
|
||||||
|
And I set "scene.BIMModelProperties.type_template" to "EMPTY"
|
||||||
|
When I press "bim.add_type"
|
||||||
|
Then the object "IfcWallType/TYPEX" is an "IfcWallType"
|
||||||
|
And the object "IfcWallType/TYPEX" has no data
|
||||||
|
|
||||||
Scenario: Enable editing type
|
Scenario: Enable editing type
|
||||||
Given an empty IFC project
|
Given an empty IFC project
|
||||||
And I add a cube
|
And I add a cube
|
||||||
@@ -171,3 +190,15 @@ Scenario: Select similar type
|
|||||||
And I press "bim.select_similar_type"
|
And I press "bim.select_similar_type"
|
||||||
Then the object "IfcWall/Cube" is selected
|
Then the object "IfcWall/Cube" is selected
|
||||||
And the object "IfcWall/Cube.001" is selected
|
And the object "IfcWall/Cube.001" is selected
|
||||||
|
|
||||||
|
Scenario: Purge unused types
|
||||||
|
Given an empty IFC project
|
||||||
|
And I press "bim.launch_type_manager"
|
||||||
|
And I set "scene.BIMModelProperties.type_class" to "IfcWallType"
|
||||||
|
And I set "scene.BIMModelProperties.type_predefined_type" to "SOLIDWALL"
|
||||||
|
And I set "scene.BIMModelProperties.type_template" to "EMPTY"
|
||||||
|
When I press "bim.add_type"
|
||||||
|
Then the object "IfcWallType/TYPEX" is an "IfcWallType"
|
||||||
|
And the object "IfcWallType/TYPEX" has no data
|
||||||
|
When I press "bim.purge_unused_types"
|
||||||
|
Then the object "IfcWallType/TYPEX" does not exist
|
||||||
|
|||||||
@@ -124,6 +124,5 @@ class TestSelectSimilarContainer:
|
|||||||
ifc.get_entity("obj").should_be_called().will_return("element")
|
ifc.get_entity("obj").should_be_called().will_return("element")
|
||||||
spatial.get_container("element").should_be_called().will_return("container")
|
spatial.get_container("element").should_be_called().will_return("container")
|
||||||
spatial.get_decomposed_elements("container").should_be_called().will_return(["contained_element"])
|
spatial.get_decomposed_elements("container").should_be_called().will_return(["contained_element"])
|
||||||
ifc.get_object("contained_element").should_be_called().will_return("contained_obj")
|
spatial.select_products(["contained_element"]).should_be_called()
|
||||||
spatial.select_object("contained_obj").should_be_called()
|
|
||||||
subject.select_similar_container(ifc, spatial, obj="obj")
|
subject.select_similar_container(ifc, spatial, obj="obj")
|
||||||
|
|||||||
@@ -39,3 +39,14 @@ class TestAssignType:
|
|||||||
ifc.get_object("element").should_be_called().will_return("obj")
|
ifc.get_object("element").should_be_called().will_return("obj")
|
||||||
type.disable_editing("obj").should_be_called()
|
type.disable_editing("obj").should_be_called()
|
||||||
subject.assign_type(ifc, type, element="element", type="type")
|
subject.assign_type(ifc, type, element="element", type="type")
|
||||||
|
|
||||||
|
|
||||||
|
class TestPurgeUnusedTypes:
|
||||||
|
def test_run(self, ifc, type):
|
||||||
|
type.get_model_types().should_be_called().will_return(["element_type"])
|
||||||
|
type.get_type_occurrences("element_type").should_be_called().will_return([])
|
||||||
|
ifc.run("root.remove_product", product="element_type").should_be_called()
|
||||||
|
ifc.get_object("element_type").should_be_called().will_return("obj")
|
||||||
|
ifc.unlink(obj="obj").should_be_called()
|
||||||
|
type.remove_object("obj").should_be_called()
|
||||||
|
subject.purge_unused_types(ifc, type)
|
||||||
|
|||||||
@@ -114,6 +114,14 @@ class TestGetIfcRepresentationClass(NewFile):
|
|||||||
assert subject.get_ifc_representation_class(ifc.createIfcColumn()) is None
|
assert subject.get_ifc_representation_class(ifc.createIfcColumn()) is None
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetModelTypes(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
|
||||||
|
assert subject.get_model_types() == [wall_type]
|
||||||
|
|
||||||
|
|
||||||
class TestGetObjectData(NewFile):
|
class TestGetObjectData(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
data = bpy.data.meshes.new("Mesh")
|
data = bpy.data.meshes.new("Mesh")
|
||||||
@@ -139,6 +147,16 @@ class TestGetRepresentationContext(NewFile):
|
|||||||
assert subject.get_representation_context(representation) == context
|
assert subject.get_representation_context(representation) == context
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetTypeOccurrences(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
|
||||||
|
wall = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run("type.assign_type", ifc, related_object=wall, relating_type=wall_type)
|
||||||
|
assert subject.get_type_occurrences(wall_type) == (wall,)
|
||||||
|
|
||||||
|
|
||||||
class TestHasMaterialUsage(NewFile):
|
class TestHasMaterialUsage(NewFile):
|
||||||
def test_getting_a_profile_set_usage(self):
|
def test_getting_a_profile_set_usage(self):
|
||||||
ifc = ifcopenshell.file()
|
ifc = ifcopenshell.file()
|
||||||
@@ -149,6 +167,13 @@ class TestHasMaterialUsage(NewFile):
|
|||||||
assert subject.has_material_usage(element) is True
|
assert subject.has_material_usage(element) is True
|
||||||
|
|
||||||
|
|
||||||
|
class TestRemoveObject(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
obj = bpy.data.objects.new("Object", None)
|
||||||
|
subject.remove_object(obj)
|
||||||
|
assert not bpy.data.objects.get("Object")
|
||||||
|
|
||||||
|
|
||||||
class TestRunGeometryAddRepresentation(NewFile):
|
class TestRunGeometryAddRepresentation(NewFile):
|
||||||
def test_nothing(self):
|
def test_nothing(self):
|
||||||
pass
|
pass
|
||||||
|
|||||||
Reference in New Issue
Block a user