Prioritise editing rendering surface styles over shading surface styles

This commit is contained in:
Dion Moult
2021-10-07 18:51:50 +11:00
parent 9508858fcb
commit f26d43e0cd
44 changed files with 1670 additions and 1468 deletions
+36 -26
View File
@@ -51,32 +51,42 @@ def draw_attribute(attribute, layout, copy_operator=None):
def import_attributes(ifc_class, props, data, callback=None): def import_attributes(ifc_class, props, data, callback=None):
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes(): for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) import_attribute(attribute, props, data, callback=callback)
if isinstance(data_type, tuple) or data_type == "entity":
callback(attribute.name(), None, data) if callback else None
continue # A more elegant attribute importer signature, intended to supersede import_attributes
new = props.add() def import_attributes2(element, props, callback=None):
new.name = attribute.name() for attribute in element.wrapped_data.declaration().as_entity().all_attributes():
new.is_null = data[attribute.name()] is None import_attribute(attribute, props, element.get_info(), callback=callback)
new.is_optional = attribute.optional()
new.data_type = data_type if isinstance(data_type, str) else ""
is_handled_by_callback = callback(attribute.name(), new, data) if callback else None def import_attribute(attribute, props, data, callback=None):
if is_handled_by_callback: data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
pass # Our job is done if isinstance(data_type, tuple) or data_type == "entity":
elif is_handled_by_callback is False: callback(attribute.name(), None, data) if callback else None
props.remove(len(props) - 1) return
elif data_type == "string": new = props.add()
new.string_value = "" if new.is_null else data[attribute.name()] new.name = attribute.name()
elif data_type == "boolean": new.is_null = data[attribute.name()] is None
new.bool_value = False if new.is_null else data[attribute.name()] new.is_optional = attribute.optional()
elif data_type == "integer": new.data_type = data_type if isinstance(data_type, str) else ""
new.int_value = 0 if new.is_null else data[attribute.name()] is_handled_by_callback = callback(attribute.name(), new, data) if callback else None
elif data_type == "float": if is_handled_by_callback:
new.float_value = 0.0 if new.is_null else data[attribute.name()] pass # Our job is done
elif data_type == "enum": elif is_handled_by_callback is False:
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute)) props.remove(len(props) - 1)
if data[attribute.name()]: elif data_type == "string":
new.enum_value = data[attribute.name()] new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "boolean":
new.bool_value = False if new.is_null else data[attribute.name()]
elif data_type == "integer":
new.int_value = 0 if new.is_null else data[attribute.name()]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
def export_attributes(props, callback=None): def export_attributes(props, callback=None):
@@ -1,3 +1,21 @@
# 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 bpy import bpy
import blenderbim.tool as tool import blenderbim.tool as tool
import ifcopenshell.util.element import ifcopenshell.util.element
@@ -40,7 +40,7 @@ class AssignObject(bpy.types.Operator, Operator):
def _execute(self, context): def _execute(self, context):
core.assign_object( core.assign_object(
tool.Ifc, tool.Ifc,
tool.Aggregator, tool.Aggregate,
tool.Collector, tool.Collector,
relating_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object)), relating_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object)),
related_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.related_object)), related_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.related_object)),
@@ -69,7 +69,7 @@ class EnableEditingAggregate(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.enable_editing_aggregate(tool.Aggregator, obj=context.active_object) core.enable_editing_aggregate(tool.Aggregate, obj=context.active_object)
class DisableEditingAggregate(bpy.types.Operator, Operator): class DisableEditingAggregate(bpy.types.Operator, Operator):
@@ -78,7 +78,7 @@ class DisableEditingAggregate(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.disable_editing_aggregate(tool.Aggregator, obj=context.active_object) core.disable_editing_aggregate(tool.Aggregate, obj=context.active_object)
class AddAggregate(bpy.types.Operator): class AddAggregate(bpy.types.Operator):
@@ -1,3 +1,21 @@
# 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 bpy import bpy
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.spatial.data import SpatialData from blenderbim.bim.module.spatial.data import SpatialData
@@ -68,7 +68,7 @@ class EnableEditingContext(bpy.types.Operator, Operator):
context: bpy.props.IntProperty() context: bpy.props.IntProperty()
def _execute(self, context): def _execute(self, context):
core.enable_editing_context(tool.ContextEditor, context=tool.Ifc.get().by_id(self.context)) core.enable_editing_context(tool.Context, context=tool.Ifc.get().by_id(self.context))
class DisableEditingContext(bpy.types.Operator, Operator): class DisableEditingContext(bpy.types.Operator, Operator):
@@ -77,7 +77,7 @@ class DisableEditingContext(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.disable_editing_context(tool.ContextEditor) core.disable_editing_context(tool.Context)
class EditContext(bpy.types.Operator, Operator): class EditContext(bpy.types.Operator, Operator):
@@ -86,4 +86,4 @@ class EditContext(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.edit_context(tool.Ifc, tool.ContextEditor) core.edit_context(tool.Ifc, tool.Context)
@@ -68,7 +68,7 @@ class AddRepresentation(bpy.types.Operator):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
bpy.ops.bim.edit_object_placement(obj=obj.name) core.edit_object_placement(tool.Ifc, tool.Surveyor, obj=obj)
if not obj.data: if not obj.data:
return {"FINISHED"} return {"FINISHED"}
@@ -39,7 +39,7 @@ class EnableEditingPerson(bpy.types.Operator, Operator):
person: bpy.props.IntProperty() person: bpy.props.IntProperty()
def _execute(self, context): def _execute(self, context):
core.enable_editing_person(tool.PersonEditor, person=tool.Ifc.get().by_id(self.person)) core.enable_editing_person(tool.Owner, person=tool.Ifc.get().by_id(self.person))
class DisableEditingPerson(bpy.types.Operator, Operator): class DisableEditingPerson(bpy.types.Operator, Operator):
@@ -48,7 +48,7 @@ class DisableEditingPerson(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.disable_editing_person(tool.PersonEditor) core.disable_editing_person(tool.Owner)
class AddPerson(bpy.types.Operator, Operator): class AddPerson(bpy.types.Operator, Operator):
@@ -66,7 +66,7 @@ class EditPerson(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.edit_person(tool.Ifc, tool.PersonEditor) core.edit_person(tool.Ifc, tool.Owner)
class RemovePerson(bpy.types.Operator, Operator): class RemovePerson(bpy.types.Operator, Operator):
@@ -86,7 +86,7 @@ class AddPersonAttribute(bpy.types.Operator, Operator):
name: bpy.props.StringProperty() name: bpy.props.StringProperty()
def _execute(self, context): def _execute(self, context):
core.add_person_attribute(tool.PersonEditor, name=self.name) core.add_person_attribute(tool.Owner, name=self.name)
class RemovePersonAttribute(bpy.types.Operator, Operator): class RemovePersonAttribute(bpy.types.Operator, Operator):
@@ -97,7 +97,7 @@ class RemovePersonAttribute(bpy.types.Operator, Operator):
id: bpy.props.IntProperty() id: bpy.props.IntProperty()
def _execute(self, context): def _execute(self, context):
core.remove_person_attribute(tool.PersonEditor, name=self.name, id=self.id) core.remove_person_attribute(tool.Owner, name=self.name, id=self.id)
class EnableEditingRole(bpy.types.Operator, Operator): class EnableEditingRole(bpy.types.Operator, Operator):
@@ -107,7 +107,7 @@ class EnableEditingRole(bpy.types.Operator, Operator):
role: bpy.props.IntProperty() role: bpy.props.IntProperty()
def _execute(self, context): def _execute(self, context):
core.enable_editing_role(tool.RoleEditor, role=tool.Ifc.get().by_id(self.role)) core.enable_editing_role(tool.Owner, role=tool.Ifc.get().by_id(self.role))
class DisableEditingRole(bpy.types.Operator, Operator): class DisableEditingRole(bpy.types.Operator, Operator):
@@ -116,7 +116,7 @@ class DisableEditingRole(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.disable_editing_role(tool.RoleEditor) core.disable_editing_role(tool.Owner)
class AddRole(bpy.types.Operator, Operator): class AddRole(bpy.types.Operator, Operator):
@@ -135,7 +135,7 @@ class EditRole(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.edit_role(tool.Ifc, tool.RoleEditor) core.edit_role(tool.Ifc, tool.Owner)
class RemoveRole(bpy.types.Operator, Operator): class RemoveRole(bpy.types.Operator, Operator):
@@ -166,7 +166,7 @@ class AddAddressAttribute(bpy.types.Operator, Operator):
name: bpy.props.StringProperty() name: bpy.props.StringProperty()
def _execute(self, context): def _execute(self, context):
core.add_address_attribute(tool.AddressEditor, name=self.name) core.add_address_attribute(tool.Owner, name=self.name)
class RemoveAddressAttribute(bpy.types.Operator, Operator): class RemoveAddressAttribute(bpy.types.Operator, Operator):
@@ -177,7 +177,7 @@ class RemoveAddressAttribute(bpy.types.Operator, Operator):
id: bpy.props.IntProperty() id: bpy.props.IntProperty()
def _execute(self, context): def _execute(self, context):
core.remove_address_attribute(tool.AddressEditor, name=self.name, id=self.id) core.remove_address_attribute(tool.Owner, name=self.name, id=self.id)
class EnableEditingAddress(bpy.types.Operator, Operator): class EnableEditingAddress(bpy.types.Operator, Operator):
@@ -187,7 +187,7 @@ class EnableEditingAddress(bpy.types.Operator, Operator):
address: bpy.props.IntProperty() address: bpy.props.IntProperty()
def _execute(self, context): def _execute(self, context):
core.enable_editing_address(tool.AddressEditor, address=tool.Ifc.get().by_id(self.address)) core.enable_editing_address(tool.Owner, address=tool.Ifc.get().by_id(self.address))
class DisableEditingAddress(bpy.types.Operator, Operator): class DisableEditingAddress(bpy.types.Operator, Operator):
@@ -196,7 +196,7 @@ class DisableEditingAddress(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.disable_editing_address(tool.AddressEditor) core.disable_editing_address(tool.Owner)
class EditAddress(bpy.types.Operator, Operator): class EditAddress(bpy.types.Operator, Operator):
@@ -205,7 +205,7 @@ class EditAddress(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.edit_address(tool.Ifc, tool.AddressEditor) core.edit_address(tool.Ifc, tool.Owner)
class RemoveAddress(bpy.types.Operator, Operator): class RemoveAddress(bpy.types.Operator, Operator):
@@ -225,7 +225,7 @@ class EnableEditingOrganisation(bpy.types.Operator, Operator):
organisation: bpy.props.IntProperty() organisation: bpy.props.IntProperty()
def _execute(self, context): def _execute(self, context):
core.enable_editing_organisation(tool.OrganisationEditor, organisation=tool.Ifc.get().by_id(self.organisation)) core.enable_editing_organisation(tool.Owner, organisation=tool.Ifc.get().by_id(self.organisation))
class DisableEditingOrganisation(bpy.types.Operator, Operator): class DisableEditingOrganisation(bpy.types.Operator, Operator):
@@ -234,7 +234,7 @@ class DisableEditingOrganisation(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.disable_editing_organisation(tool.OrganisationEditor) core.disable_editing_organisation(tool.Owner)
class AddOrganisation(bpy.types.Operator, Operator): class AddOrganisation(bpy.types.Operator, Operator):
@@ -252,7 +252,7 @@ class EditOrganisation(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.edit_organisation(tool.Ifc, tool.OrganisationEditor) core.edit_organisation(tool.Ifc, tool.Owner)
class RemoveOrganisation(bpy.types.Operator, Operator): class RemoveOrganisation(bpy.types.Operator, Operator):
@@ -96,13 +96,13 @@ class CreateProject(bpy.types.Operator):
bpy.ops.bim.assign_class(obj=building_storey.name, ifc_class="IfcBuildingStorey") bpy.ops.bim.assign_class(obj=building_storey.name, ifc_class="IfcBuildingStorey")
blenderbim.core.aggregate.assign_object( blenderbim.core.aggregate.assign_object(
tool.Ifc, tool.Aggregator, tool.Collector, relating_obj=project, related_obj=site tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=project, related_obj=site
) )
blenderbim.core.aggregate.assign_object( blenderbim.core.aggregate.assign_object(
tool.Ifc, tool.Aggregator, tool.Collector, relating_obj=site, related_obj=building tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=site, related_obj=building
) )
blenderbim.core.aggregate.assign_object( blenderbim.core.aggregate.assign_object(
tool.Ifc, tool.Aggregator, tool.Collector, relating_obj=building, related_obj=building_storey tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=building, related_obj=building_storey
) )
context.view_layer.objects.active = active_object context.view_layer.objects.active = active_object
@@ -204,7 +204,13 @@ class AssignClass(bpy.types.Operator):
collection.objects.link(obj) collection.objects.link(obj)
if parent_collection: if parent_collection:
parent_collection.children.link(collection) parent_collection.children.link(collection)
bpy.ops.bim.assign_object(related_object=obj.name, relating_object=parent_collection.name) blenderbim.core.aggregate.assign_object(
tool.Ifc,
tool.Aggregator,
tool.Collector,
relating_obj=bpy.data.objects.get(parent_collection.name),
related_obj=obj,
)
else: else:
context.scene.collection.children.link(collection) context.scene.collection.children.link(collection)
@@ -17,31 +17,27 @@
# 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 bpy
import blenderbim.core.tool
import blenderbim.tool as tool import blenderbim.tool as tool
import blenderbim.bim.helper
class RoleEditor(blenderbim.core.tool.RoleEditor): def refresh():
@classmethod StyleAttributesData.is_loaded = False
def set_role(cls, role):
bpy.context.scene.BIMOwnerProperties.active_role_id = role.id()
class StyleAttributesData:
data = {}
is_loaded = False
@classmethod @classmethod
def import_attributes(cls): def load(cls):
role = cls.get_role() cls.data = {"attributes": cls.get_attributes()}
props = bpy.context.scene.BIMOwnerProperties
props.role_attributes.clear()
blenderbim.bim.helper.import_attributes("IfcActorRole", props.role_attributes, role.get_info())
@classmethod @classmethod
def clear_role(cls): def get_attributes(cls):
bpy.context.scene.BIMOwnerProperties.active_role_id = 0 style = tool.Ifc.get().by_id(bpy.context.active_object.active_material.BIMMaterialProperties.ifc_style_id)
results = []
@classmethod for name, value in style.get_info().items():
def get_role(cls): if name in ["id", "type", "Styles"]:
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_role_id) continue
results.append({"name": name, "value": str(value)})
@classmethod return results
def export_attributes(cls):
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMOwnerProperties.role_attributes)
@@ -17,174 +17,77 @@
# 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 bpy
import ifcopenshell.api import blenderbim.bim.handler
import ifcopenshell.util.representation import blenderbim.tool as tool
import blenderbim.bim.helper import blenderbim.core.style as core
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.style.data import Data
def get_colour_settings(material): class Operator:
transparency = material.diffuse_color[3] def execute(self, context):
diffuse_colour = material.diffuse_color IfcStore.execute_ifc_operator(self, context)
if material.use_nodes and hasattr(material.node_tree, "nodes") and "Principled BSDF" in material.node_tree.nodes: blenderbim.bim.handler.refresh_ui_data()
bsdf = material.node_tree.nodes["Principled BSDF"] return {"FINISHED"}
transparency = bsdf.inputs["Alpha"].default_value
diffuse_colour = bsdf.inputs["Base Color"].default_value
transparency = 1 - transparency
return {
"surface_colour": tuple(material.diffuse_color),
"transparency": transparency,
"diffuse_colour": tuple(diffuse_colour),
}
class UpdateStyleColours(bpy.types.Operator): class UpdateStyleColours(bpy.types.Operator, Operator):
bl_idname = "bim.update_style_colours" bl_idname = "bim.update_style_colours"
bl_label = "Update Style Colours" bl_label = "Update Style Colours"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
material: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.update_style_colours(tool.Ifc, tool.Style, obj=context.active_object.active_material)
material = bpy.data.materials.get(self.material) if self.material else context.active_object.active_material
settings = get_colour_settings(material)
for style in self.file.by_id(material.BIMMaterialProperties.ifc_style_id).Styles:
if style.is_a("IfcSurfaceStyleRendering"):
ifcopenshell.api.run(
"style.edit_surface_style",
self.file,
style=style,
attributes={
"SurfaceColour": settings["surface_colour"],
"Transparency": settings["transparency"],
"DiffuseColour": settings["diffuse_colour"],
},
)
elif style.is_a("IfcSurfaceStyleShading"):
ifcopenshell.api.run(
"style.edit_surface_style",
self.file,
style=style,
attributes={"SurfaceColour": settings["surface_colour"], "Transparency": settings["transparency"]},
)
return {"FINISHED"}
class RemoveStyle(bpy.types.Operator): class RemoveStyle(bpy.types.Operator, Operator):
bl_idname = "bim.remove_style" bl_idname = "bim.remove_style"
bl_label = "Remove Style" bl_label = "Remove Style"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
material: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.remove_style(tool.Ifc, tool.Style, obj=context.active_object.active_material)
material = bpy.data.materials.get(self.material) if self.material else context.active_object.active_material
ifcopenshell.api.run(
"style.remove_style", self.file, style=self.file.by_id(material.BIMMaterialProperties.ifc_style_id)
)
material.BIMMaterialProperties.ifc_style_id = 0
return {"FINISHED"}
class AddStyle(bpy.types.Operator): class AddStyle(bpy.types.Operator, Operator):
bl_idname = "bim.add_style" bl_idname = "bim.add_style"
bl_label = "Add Style" bl_label = "Add Style"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
material: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.add_style(tool.Ifc, tool.Style, obj=context.active_object.active_material)
material = bpy.data.materials.get(self.material) if self.material else context.active_object.active_material
settings = get_colour_settings(material)
settings["name"] = material.name
settings["external_definition"] = None # TODO: Implement. See #1222
style = ifcopenshell.api.run("style.add_style", self.file, **settings)
IfcStore.link_element(style, material)
if material.BIMObjectProperties.ifc_definition_id:
context = ifcopenshell.util.representation.get_context(self.file, "Model", "Body", "MODEL_VIEW")
if context:
ifcopenshell.api.run(
"style.assign_material_style",
self.file,
**{
"material": self.file.by_id(material.BIMObjectProperties.ifc_definition_id),
"style": style,
"context": context,
}
)
return {"FINISHED"}
class UnlinkStyle(bpy.types.Operator): class UnlinkStyle(bpy.types.Operator, Operator):
bl_idname = "bim.unlink_style" bl_idname = "bim.unlink_style"
bl_label = "Unlink Style" bl_label = "Unlink Style"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
material: bpy.props.StringProperty()
def execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.unlink_style(tool.Style, obj=context.active_object.active_material)
material = bpy.data.materials.get(self.material)
material.BIMMaterialProperties.ifc_style_id = 0
if "Ifc" in material.name and "/" in material.name:
material.name = "/".join(material.name.split("/")[1:])
return {"FINISHED"}
class EnableEditingStyle(bpy.types.Operator): class EnableEditingStyle(bpy.types.Operator, Operator):
bl_idname = "bim.enable_editing_style" bl_idname = "bim.enable_editing_style"
bl_label = "Enable Editing Style" bl_label = "Enable Editing Style"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
material: bpy.props.StringProperty()
def execute(self, context): def _execute(self, context):
material = bpy.data.materials.get(self.material) if self.material else context.active_object.active_material core.enable_editing_style(tool.Style, obj=context.active_object.active_material)
props = material.BIMStyleProperties
props.attributes.clear()
data = Data.styles[material.BIMMaterialProperties.ifc_style_id]
blenderbim.bim.helper.import_attributes("IfcSurfaceStyle", props.attributes, data)
props.is_editing_attributes = True
return {"FINISHED"}
class DisableEditingStyle(bpy.types.Operator): class DisableEditingStyle(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_style" bl_idname = "bim.disable_editing_style"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
bl_label = "Disable Editing Style" bl_label = "Disable Editing Style"
material: bpy.props.StringProperty()
def execute(self, context): def _execute(self, context):
material = bpy.data.materials.get(self.material) if self.material else context.active_object.active_material core.disable_editing_style(tool.Style, obj=context.active_object.active_material)
props = material.BIMStyleProperties
props.is_editing_attributes = False
return {"FINISHED"}
class EditStyle(bpy.types.Operator): class EditStyle(bpy.types.Operator, Operator):
bl_idname = "bim.edit_style" bl_idname = "bim.edit_style"
bl_label = "Edit Style" bl_label = "Edit Style"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
material = context.active_object.active_material core.edit_style(tool.Ifc, tool.Style, obj=context.active_object.active_material)
props = material.BIMStyleProperties
attributes = blenderbim.bim.helper.export_attributes(props.attributes)
self.file = IfcStore.get_file()
style = self.file.by_id(material.BIMMaterialProperties.ifc_style_id)
ifcopenshell.api.run("style.edit_presentation_style", self.file, **{"style": style, "attributes": attributes})
Data.load(IfcStore.get_file(), material.BIMMaterialProperties.ifc_style_id)
bpy.ops.bim.disable_editing_style()
return {"FINISHED"}
@@ -34,4 +34,4 @@ from bpy.props import (
class BIMStyleProperties(PropertyGroup): class BIMStyleProperties(PropertyGroup):
attributes: CollectionProperty(name="Attributes", type=Attribute) attributes: CollectionProperty(name="Attributes", type=Attribute)
is_editing_attributes: BoolProperty(name="Is Editing Attributes") is_editing: BoolProperty(name="Is Editing")
@@ -19,7 +19,7 @@
import blenderbim.bim.helper import blenderbim.bim.helper
from bpy.types import Panel from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.style.data import Data from blenderbim.bim.module.style.data import StyleAttributesData
class BIM_PT_style(Panel): class BIM_PT_style(Panel):
@@ -38,13 +38,12 @@ class BIM_PT_style(Panel):
) )
def draw(self, context): def draw(self, context):
material = context.active_object.active_material props = context.active_object.active_material.BIMMaterialProperties
props = material.BIMMaterialProperties
row = self.layout.row(align=True) row = self.layout.row(align=True)
if props.ifc_style_id: if props.ifc_style_id:
row.operator("bim.update_style_colours", icon="GREASEPENCIL").material = material.name row.operator("bim.update_style_colours", icon="GREASEPENCIL")
row.operator("bim.unlink_style", icon="UNLINKED", text="").material = material.name row.operator("bim.unlink_style", icon="UNLINKED", text="")
row.operator("bim.remove_style", icon="X", text="").material = material.name row.operator("bim.remove_style", icon="X", text="")
else: else:
row.operator("bim.add_style", icon="ADD") row.operator("bim.add_style", icon="ADD")
@@ -66,12 +65,13 @@ class BIM_PT_style_attributes(Panel):
return False return False
def draw(self, context): def draw(self, context):
if not StyleAttributesData.is_loaded:
StyleAttributesData.load()
obj = context.active_object.active_material obj = context.active_object.active_material
mprops = obj.BIMMaterialProperties mprops = obj.BIMMaterialProperties
props = obj.BIMStyleProperties props = obj.BIMStyleProperties
if mprops.ifc_style_id not in Data.styles: if props.is_editing:
Data.load(IfcStore.get_file(), mprops.ifc_style_id)
if props.is_editing_attributes:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.operator("bim.edit_style", icon="CHECKMARK") row.operator("bim.edit_style", icon="CHECKMARK")
row.operator("bim.disable_editing_style", icon="CANCEL", text="") row.operator("bim.disable_editing_style", icon="CANCEL", text="")
@@ -84,9 +84,7 @@ class BIM_PT_style_attributes(Panel):
row.label(text="STEP ID") row.label(text="STEP ID")
row.label(text=str(mprops.ifc_style_id)) row.label(text=str(mprops.ifc_style_id))
for name, value in Data.styles[mprops.ifc_style_id].items(): for attribute in StyleAttributesData.data["attributes"]:
if name in ["id", "type", "Styles"]:
continue
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text=name) row.label(text=attribute["name"])
row.label(text=str(value)) row.label(text=attribute["value"])
@@ -1,3 +1,22 @@
# 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/>.
def enable_editing_aggregate(aggregator, obj=None): def enable_editing_aggregate(aggregator, obj=None):
aggregator.enable_editing(obj) aggregator.enable_editing(obj)
+8 -8
View File
@@ -31,15 +31,15 @@ def remove_context(ifc, context=None):
ifc.run("context.remove_context", context=context) ifc.run("context.remove_context", context=context)
def enable_editing_context(context_editor, context=None): def enable_editing_context(context_tool, context=None):
context_editor.set_context(context) context_tool.set_context(context)
context_editor.import_attributes() context_tool.import_attributes()
def disable_editing_context(context_editor): def disable_editing_context(context):
context_editor.clear_context() context.clear_context()
def edit_context(ifc, context_editor): def edit_context(ifc, context):
ifc.run("context.edit_context", context=context_editor.get_context(), attributes=context_editor.export_attributes()) ifc.run("context.edit_context", context=context.get_context(), attributes=context.export_attributes())
disable_editing_context(context_editor) disable_editing_context(context)
+42 -42
View File
@@ -25,26 +25,26 @@ def remove_person(ifc, person=None):
ifc.run("owner.remove_person", person=person) ifc.run("owner.remove_person", person=person)
def enable_editing_person(person_editor, person=None): def enable_editing_person(owner, person=None):
person_editor.set_person(person) owner.set_person(person)
person_editor.import_attributes() owner.import_person_attributes()
def disable_editing_person(person_editor): def disable_editing_person(owner):
person_editor.clear_person() owner.clear_person()
def edit_person(ifc, person_editor): def edit_person(ifc, owner):
ifc.run("owner.edit_person", person=person_editor.get_person(), attributes=person_editor.export_attributes()) ifc.run("owner.edit_person", person=owner.get_person(), attributes=owner.export_person_attributes())
disable_editing_person(person_editor) disable_editing_person(owner)
def add_person_attribute(person_editor, name=None): def add_person_attribute(owner, name=None):
person_editor.add_attribute(name) owner.add_person_attribute(name)
def remove_person_attribute(person_editor, name=None, id=None): def remove_person_attribute(owner, name=None, id=None):
person_editor.remove_attribute(name, id) owner.remove_person_attribute(name, id)
def add_role(ifc, parent=None): def add_role(ifc, parent=None):
@@ -55,18 +55,18 @@ def remove_role(ifc, role=None):
ifc.run("owner.remove_role", role=role) ifc.run("owner.remove_role", role=role)
def enable_editing_role(role_editor, role=None): def enable_editing_role(owner, role=None):
role_editor.set_role(role) owner.set_role(role)
role_editor.import_attributes() owner.import_role_attributes()
def disable_editing_role(role_editor): def disable_editing_role(owner):
role_editor.clear_role() owner.clear_role()
def edit_role(ifc, role_editor): def edit_role(ifc, owner):
ifc.run("owner.edit_role", role=role_editor.get_role(), attributes=role_editor.export_attributes()) ifc.run("owner.edit_role", role=owner.get_role(), attributes=owner.export_role_attributes())
role_editor.clear_role() owner.clear_role()
def add_address(ifc, parent=None, ifc_class="IfcPostalAddress"): def add_address(ifc, parent=None, ifc_class="IfcPostalAddress"):
@@ -77,27 +77,27 @@ def remove_address(ifc, address=None):
ifc.run("owner.remove_address", address=address) ifc.run("owner.remove_address", address=address)
def enable_editing_address(address_editor, address=None): def enable_editing_address(owner, address=None):
address_editor.set_address(address) owner.set_address(address)
address_editor.import_attributes() owner.import_address_attributes()
def disable_editing_address(address_editor): def disable_editing_address(owner):
address_editor.clear_address() owner.clear_address()
def edit_address(ifc, address_editor): def edit_address(ifc, owner):
address = address_editor.get_address() address = owner.get_address()
ifc.run("owner.edit_address", address=address, attributes=address_editor.export_attributes()) ifc.run("owner.edit_address", address=address, attributes=owner.export_address_attributes())
address_editor.clear_address() owner.clear_address()
def add_address_attribute(address_editor, name=None): def add_address_attribute(owner, name=None):
address_editor.add_attribute(name) owner.add_address_attribute(name)
def remove_address_attribute(address_editor, name=None, id=None): def remove_address_attribute(owner, name=None, id=None):
address_editor.remove_attribute(name, id) owner.remove_address_attribute(name, id)
def add_organisation(ifc): def add_organisation(ifc):
@@ -108,19 +108,19 @@ def remove_organisation(ifc, organisation=None):
ifc.run("owner.remove_organisation", organisation=organisation) ifc.run("owner.remove_organisation", organisation=organisation)
def enable_editing_organisation(organisation_editor, organisation=None): def enable_editing_organisation(owner, organisation=None):
organisation_editor.set_organisation(organisation) owner.set_organisation(organisation)
organisation_editor.import_attributes() owner.import_organisation_attributes()
def disable_editing_organisation(organisation_editor): def disable_editing_organisation(owner):
organisation_editor.clear_organisation() owner.clear_organisation()
def edit_organisation(ifc, organisation_editor): def edit_organisation(ifc, owner):
organisation = organisation_editor.get_organisation() organisation = owner.get_organisation()
ifc.run("owner.edit_organisation", organisation=organisation, attributes=organisation_editor.export_attributes()) ifc.run("owner.edit_organisation", organisation=organisation, attributes=owner.export_organisation_attributes())
organisation_editor.clear_organisation() owner.clear_organisation()
def add_person_and_organisation(ifc, person=None, organisation=None): def add_person_and_organisation(ifc, person=None, organisation=None):
+69
View File
@@ -0,0 +1,69 @@
# 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/>.
def add_style(ifc, style, obj=None):
element = ifc.run("style.add_style", name=style.get_name(obj))
style.link(element, obj)
ifc.run(
"style.add_surface_style",
style=element,
ifc_class="IfcSurfaceStyleRendering",
attributes=style.get_surface_rendering_attributes(obj),
)
material = ifc.get_entity(obj)
if material:
ifc.run("style.assign_material_style", material=material, style=element, context=style.get_context(obj))
return element
def remove_style(ifc, style, obj=None):
ifc.run("style.remove_style", style=style.get_style(obj))
style.unlink(obj=obj)
def update_style_colours(ifc, style, obj=None):
rendering_style = style.get_surface_rendering_style(obj)
if rendering_style:
attributes = style.get_surface_rendering_attributes(obj)
ifc.run("style.edit_surface_style", style=rendering_style, attributes=attributes)
return
shading_style = style.get_surface_shading_style(obj)
if shading_style:
attributes = style.get_surface_shading_attributes(obj)
ifc.run("style.edit_surface_style", style=shading_style, attributes=attributes)
def unlink_style(style, obj=None):
style.unlink(obj)
def enable_editing_style(style, obj=None):
style.enable_editing(obj)
style.import_surface_attributes(style.get_style(obj), obj)
def disable_editing_style(style, obj=None):
style.disable_editing(obj)
def edit_style(ifc, style, obj=None):
attributes = style.export_surface_attributes(obj)
ifc.run("style.edit_presentation_style", style=style.get_style(obj), attributes=attributes)
style.disable_editing(obj)
+97 -248
View File
@@ -17,273 +17,122 @@
# 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 abc import abc
import inspect
# fmt: off
# pylint: skip-file
# This interface class and decorator is magic syntatic sugar to allow concise interface definitions
# If we didn't do this, Python is unnecessarily verbose, which I find distracting. Don't black this file :)
class Interface(abc.ABC): pass
def interface(cls):
attrs = {n: classmethod(abc.abstractmethod(f)) for n, f in inspect.getmembers(cls, predicate=inspect.isfunction)}
return type(cls.__name__, (Interface, cls), attrs)
class AddressEditor(abc.ABC): @interface
@classmethod class Aggregate:
@abc.abstractmethod def enable_editing(cls, obj): pass
def set_address(cls, address): def disable_editing(cls, obj): pass
pass def can_aggregate(cls, relating_object, related_object): pass
@classmethod
@abc.abstractmethod
def import_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def clear_address(cls):
pass
@classmethod
@abc.abstractmethod
def get_address(cls):
pass
@classmethod
@abc.abstractmethod
def export_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def add_attribute(cls, name):
pass
@classmethod
@abc.abstractmethod
def remove_attribute(cls, name, id):
pass
class Aggregator(abc.ABC): @interface
@classmethod class Blender: pass
@abc.abstractmethod
def enable_editing(cls, obj):
pass
@classmethod
@abc.abstractmethod
def disable_editing(cls, obj):
pass
@classmethod
@abc.abstractmethod
def can_aggregate(cls, relating_object, related_object):
pass
class Blender(abc.ABC): @interface
pass class Collector:
def assign(cls, obj): pass
class Collector(abc.ABC): @interface
@classmethod class Container:
@abc.abstractmethod def can_contain(cls, structure_obj, element_obj): pass
def assign(cls, obj): def enable_editing(cls, obj): pass
pass def disable_editing(cls, obj): pass
def import_containers(cls, parent=None): pass
class Container(abc.ABC): @interface
@classmethod class Context:
@abc.abstractmethod def set_context(cls, context): pass
def can_contain(cls, structure_obj, element_obj): def import_attributes(cls): pass
pass def clear_context(cls): pass
def get_context(cls): pass
@classmethod def export_attributes(cls): pass
@abc.abstractmethod
def enable_editing(cls, obj):
pass
@classmethod
@abc.abstractmethod
def disable_editing(cls, obj):
pass
@classmethod
@abc.abstractmethod
def import_containers(cls, parent=None):
pass
class ContextEditor(abc.ABC): @interface
@classmethod class Ifc:
@abc.abstractmethod def run(cls, command, **kwargs): pass
def set_context(cls, context): def get(cls): pass
pass def get_entity(cls, obj): pass
def link(cls, element, obj): pass
@classmethod def unlink(cls, element=None, obj=None): pass
@abc.abstractmethod
def import_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def clear_context(cls):
pass
@classmethod
@abc.abstractmethod
def get_context(cls):
pass
@classmethod
@abc.abstractmethod
def export_attributes(cls):
pass
class Ifc(abc.ABC): @interface
@classmethod class Owner:
@abc.abstractmethod def set_user(cls, user): pass
def run(cls, command, **kwargs): def get_user(cls): pass
pass def clear_user(cls): pass
def add_address_attribute(cls, name): pass
@classmethod def clear_address(cls): pass
@abc.abstractmethod def export_address_attributes(cls): pass
def get_entity(cls, obj): def get_address(cls): pass
pass def import_address_attributes(cls): pass
def remove_address_attribute(cls, name, id): pass
def set_address(cls, address): pass
def set_organisation(cls, organisation): pass
def import_organisation_attributes(cls): pass
def clear_organisation(cls): pass
def get_organisation(cls): pass
def export_organisation_attributes(cls): pass
def set_person(cls, person): pass
def import_person_attributes(cls): pass
def clear_person(cls): pass
def export_person_attributes(cls): pass
def get_person(cls): pass
def add_person_attribute(cls, name): pass
def remove_person_attribute(cls, name, id): pass
def set_role(cls, role): pass
def import_role_attributes(cls): pass
def clear_role(cls): pass
def get_role(cls): pass
def export_role_attributes(cls): pass
class OrganisationEditor(abc.ABC): @interface
@classmethod class Selector:
@abc.abstractmethod def set_active(cls, obj): pass
def set_organisation(cls, organisation):
pass
@classmethod
@abc.abstractmethod
def import_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def clear_organisation(cls):
pass
@classmethod
@abc.abstractmethod
def get_organisation(cls):
pass
@classmethod
@abc.abstractmethod
def export_attributes(cls):
pass
class Owner(abc.ABC): @interface
@classmethod class Style:
@abc.abstractmethod def disable_editing(cls, obj): pass
def set_user(cls, user): def enable_editing(cls, obj): pass
pass def export_surface_attributes(cls, obj): pass
def get_context(cls, obj): pass
@classmethod def get_name(cls, obj): pass
@abc.abstractmethod def get_style(cls, obj): pass
def get_user(cls): def get_surface_rendering_attributes(cls, obj): pass
pass def get_surface_rendering_style(cls, obj): pass
def get_surface_shading_attributes(cls, obj): pass
@classmethod def get_surface_shading_style(cls, obj): pass
@abc.abstractmethod def import_surface_attributes(cls, style, obj): pass
def clear_user(cls): def link(cls, style, obj): pass
pass def unlink(cls, obj): pass
class PersonEditor(abc.ABC): @interface
@classmethod class Surveyor:
@abc.abstractmethod def get_absolute_matrix(cls, obj): pass
def set_person(cls, person):
pass
@classmethod
@abc.abstractmethod
def import_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def clear_person(cls):
pass
@classmethod
@abc.abstractmethod
def export_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def get_person(cls):
pass
@classmethod
@abc.abstractmethod
def add_attribute(cls, name):
pass
@classmethod
@abc.abstractmethod
def remove_attribute(cls, name, id):
pass
class RoleEditor(abc.ABC): @interface
@classmethod class Voider:
@abc.abstractmethod def can_void(cls, opening, element): pass
def set_role(cls, role): def void(cls, opening_obj, building_obj): pass
pass def unvoid(cls, opening_obj): pass
def set_void_display(cls, opening_obj): pass
@classmethod
@abc.abstractmethod
def import_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def clear_role(cls):
pass
@classmethod
@abc.abstractmethod
def get_role(cls):
pass
@classmethod
@abc.abstractmethod
def export_attributes(cls):
pass
class Selector(abc.ABC):
@classmethod
@abc.abstractmethod
def set_active(cls, obj):
pass
class Surveyor(abc.ABC):
@classmethod
@abc.abstractmethod
def get_absolute_matrix(cls, obj):
pass
class Voider(abc.ABC):
@classmethod
@abc.abstractmethod
def can_void(cls, opening, element):
pass
@classmethod
@abc.abstractmethod
def void(cls, opening_obj, building_obj):
pass
@classmethod
@abc.abstractmethod
def unvoid(cls, opening_obj):
pass
@classmethod
@abc.abstractmethod
def set_void_display(cls, opening_obj):
pass
+3 -6
View File
@@ -16,15 +16,12 @@
# 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/>.
from blenderbim.tool.address_editor import AddressEditor from blenderbim.tool.aggregate import Aggregate
from blenderbim.tool.aggregator import Aggregator
from blenderbim.tool.blender import Blender from blenderbim.tool.blender import Blender
from blenderbim.tool.collector import Collector from blenderbim.tool.collector import Collector
from blenderbim.tool.container import Container from blenderbim.tool.container import Container
from blenderbim.tool.context_editor import ContextEditor from blenderbim.tool.context import Context
from blenderbim.tool.ifc import Ifc from blenderbim.tool.ifc import Ifc
from blenderbim.tool.organisation_editor import OrganisationEditor
from blenderbim.tool.owner import Owner from blenderbim.tool.owner import Owner
from blenderbim.tool.person_editor import PersonEditor from blenderbim.tool.style import Style
from blenderbim.tool.role_editor import RoleEditor
from blenderbim.tool.surveyor import Surveyor from blenderbim.tool.surveyor import Surveyor
@@ -1,108 +0,0 @@
# 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 bpy
import blenderbim.core.tool
import blenderbim.bim.helper
import blenderbim.tool as tool
class AddressEditor(blenderbim.core.tool.AddressEditor):
@classmethod
def set_address(cls, address):
bpy.context.scene.BIMOwnerProperties.active_address_id = address.id()
@classmethod
def import_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
props.address_attributes.clear()
props.address_lines.clear()
props.telephone_numbers.clear()
props.facsimile_numbers.clear()
props.electronic_mail_addresses.clear()
props.messaging_ids.clear()
address = cls.get_address()
def callback(name, prop, data):
if name == "AddressLines":
for line in data[name] or []:
props.address_lines.add().name = line
elif name == "TelephoneNumbers":
for line in data[name] or []:
props.telephone_numbers.add().name = line
elif name == "FacsimileNumbers":
for line in data[name] or []:
props.facsimile_numbers.add().name = line
elif name == "ElectronicMailAddresses":
for line in data[name] or []:
props.electronic_mail_addresses.add().name = line
elif name == "MessagingIDs":
for line in data[name] or []:
props.messaging_ids.add().name = line
blenderbim.bim.helper.import_attributes(address.is_a(), props.address_attributes, address.get_info(), callback)
@classmethod
def clear_address(cls):
bpy.context.scene.BIMOwnerProperties.active_address_id = 0
@classmethod
def get_address(cls):
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_address_id)
@classmethod
def export_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
attributes = blenderbim.bim.helper.export_attributes(props.address_attributes)
if cls.get_address().is_a("IfcPostalAddress"):
attributes["AddressLines"] = [l.name for l in props.address_lines] or None
elif cls.get_address().is_a("IfcTelecomAddress"):
attributes["TelephoneNumbers"] = [l.name for l in props.telephone_numbers] or None
attributes["FacsimileNumbers"] = [l.name for l in props.facsimile_numbers] or None
attributes["ElectronicMailAddresses"] = [l.name for l in props.electronic_mail_addresses] or None
attributes["MessagingIDs"] = [l.name for l in props.messaging_ids] or None
return attributes
@classmethod
def add_attribute(cls, name):
props = bpy.context.scene.BIMOwnerProperties
if name == "AddressLines":
props.address_lines.add()
elif name == "TelephoneNumbers":
props.telephone_numbers.add()
elif name == "FacsimileNumbers":
props.facsimile_numbers.add()
elif name == "ElectronicMailAddresses":
props.electronic_mail_addresses.add()
elif name == "MessagingIDs":
props.messaging_ids.add()
@classmethod
def remove_attribute(cls, name, id):
props = bpy.context.scene.BIMOwnerProperties
if name == "AddressLines":
props.address_lines.remove(id)
elif name == "TelephoneNumbers":
props.telephone_numbers.remove(id)
elif name == "FacsimileNumbers":
props.facsimile_numbers.remove(id)
elif name == "ElectronicMailAddresses":
props.electronic_mail_addresses.remove(id)
elif name == "MessagingIDs":
props.messaging_ids.remove(id)
@@ -1,8 +1,26 @@
# 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.tool import blenderbim.core.tool
import blenderbim.tool as tool import blenderbim.tool as tool
class Aggregator(blenderbim.core.tool.Aggregator): class Aggregate(blenderbim.core.tool.Aggregate):
@classmethod @classmethod
def enable_editing(cls, obj): def enable_editing(cls, obj):
obj.BIMObjectAggregateProperties.is_editing = True obj.BIMObjectAggregateProperties.is_editing = True
@@ -22,7 +22,7 @@ import blenderbim.tool as tool
import blenderbim.core.tool import blenderbim.core.tool
class ContextEditor(blenderbim.core.tool.ContextEditor): class Context(blenderbim.core.tool.Context):
@classmethod @classmethod
def set_context(cls, context): def set_context(cls, context):
bpy.context.scene.BIMContextProperties.active_context_id = context.id() bpy.context.scene.BIMContextProperties.active_context_id = context.id()
@@ -1,53 +0,0 @@
# 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 bpy
import ifcopenshell.api
import blenderbim.core.tool
import blenderbim.bim.helper
import blenderbim.tool as tool
class OrganisationEditor(blenderbim.core.tool.OrganisationEditor):
@classmethod
def set_organisation(cls, organisation):
bpy.context.scene.BIMOwnerProperties.active_organisation_id = organisation.id()
@classmethod
def import_attributes(cls):
organisation = tool.Ifc.get().by_id(bpy.context.scene.BIMOwnerProperties.active_organisation_id)
props = bpy.context.scene.BIMOwnerProperties
props.organisation_attributes.clear()
blenderbim.bim.helper.import_attributes(
"IfcOrganization", props.organisation_attributes, organisation.get_info()
)
@classmethod
def clear_organisation(cls):
bpy.context.scene.BIMOwnerProperties.active_organisation_id = 0
@classmethod
def export_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
attributes = blenderbim.bim.helper.export_attributes(props.organisation_attributes)
return attributes
@classmethod
def get_organisation(cls):
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_organisation_id)
+196
View File
@@ -34,3 +34,199 @@ class Owner(blenderbim.core.tool.Owner):
@classmethod @classmethod
def clear_user(cls): def clear_user(cls):
bpy.context.scene.BIMOwnerProperties.active_user_id = 0 bpy.context.scene.BIMOwnerProperties.active_user_id = 0
@classmethod
def set_address(cls, address):
bpy.context.scene.BIMOwnerProperties.active_address_id = address.id()
@classmethod
def import_address_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
props.address_attributes.clear()
props.address_lines.clear()
props.telephone_numbers.clear()
props.facsimile_numbers.clear()
props.electronic_mail_addresses.clear()
props.messaging_ids.clear()
address = cls.get_address()
def callback(name, prop, data):
if name == "AddressLines":
for line in data[name] or []:
props.address_lines.add().name = line
elif name == "TelephoneNumbers":
for line in data[name] or []:
props.telephone_numbers.add().name = line
elif name == "FacsimileNumbers":
for line in data[name] or []:
props.facsimile_numbers.add().name = line
elif name == "ElectronicMailAddresses":
for line in data[name] or []:
props.electronic_mail_addresses.add().name = line
elif name == "MessagingIDs":
for line in data[name] or []:
props.messaging_ids.add().name = line
blenderbim.bim.helper.import_attributes(address.is_a(), props.address_attributes, address.get_info(), callback)
@classmethod
def clear_address(cls):
bpy.context.scene.BIMOwnerProperties.active_address_id = 0
@classmethod
def get_address(cls):
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_address_id)
@classmethod
def export_address_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
attributes = blenderbim.bim.helper.export_attributes(props.address_attributes)
if cls.get_address().is_a("IfcPostalAddress"):
attributes["AddressLines"] = [l.name for l in props.address_lines] or None
elif cls.get_address().is_a("IfcTelecomAddress"):
attributes["TelephoneNumbers"] = [l.name for l in props.telephone_numbers] or None
attributes["FacsimileNumbers"] = [l.name for l in props.facsimile_numbers] or None
attributes["ElectronicMailAddresses"] = [l.name for l in props.electronic_mail_addresses] or None
attributes["MessagingIDs"] = [l.name for l in props.messaging_ids] or None
return attributes
@classmethod
def add_address_attribute(cls, name):
props = bpy.context.scene.BIMOwnerProperties
if name == "AddressLines":
props.address_lines.add()
elif name == "TelephoneNumbers":
props.telephone_numbers.add()
elif name == "FacsimileNumbers":
props.facsimile_numbers.add()
elif name == "ElectronicMailAddresses":
props.electronic_mail_addresses.add()
elif name == "MessagingIDs":
props.messaging_ids.add()
@classmethod
def remove_address_attribute(cls, name, id):
props = bpy.context.scene.BIMOwnerProperties
if name == "AddressLines":
props.address_lines.remove(id)
elif name == "TelephoneNumbers":
props.telephone_numbers.remove(id)
elif name == "FacsimileNumbers":
props.facsimile_numbers.remove(id)
elif name == "ElectronicMailAddresses":
props.electronic_mail_addresses.remove(id)
elif name == "MessagingIDs":
props.messaging_ids.remove(id)
@classmethod
def set_organisation(cls, organisation):
bpy.context.scene.BIMOwnerProperties.active_organisation_id = organisation.id()
@classmethod
def import_organisation_attributes(cls):
organisation = tool.Ifc.get().by_id(bpy.context.scene.BIMOwnerProperties.active_organisation_id)
props = bpy.context.scene.BIMOwnerProperties
props.organisation_attributes.clear()
blenderbim.bim.helper.import_attributes(
"IfcOrganization", props.organisation_attributes, organisation.get_info()
)
@classmethod
def clear_organisation(cls):
bpy.context.scene.BIMOwnerProperties.active_organisation_id = 0
@classmethod
def export_organisation_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
attributes = blenderbim.bim.helper.export_attributes(props.organisation_attributes)
return attributes
@classmethod
def get_organisation(cls):
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_organisation_id)
@classmethod
def set_person(cls, person):
bpy.context.scene.BIMOwnerProperties.active_person_id = person.id()
@classmethod
def import_person_attributes(cls):
person = tool.Ifc.get().by_id(bpy.context.scene.BIMOwnerProperties.active_person_id)
props = bpy.context.scene.BIMOwnerProperties
props.person_attributes.clear()
props.middle_names.clear()
props.prefix_titles.clear()
props.suffix_titles.clear()
def callback(name, prop, data):
if name == "MiddleNames":
for name in data["MiddleNames"] or []:
props.middle_names.add().name = name or ""
if name == "PrefixTitles":
for name in data["PrefixTitles"] or []:
props.prefix_titles.add().name = name or ""
if name == "SuffixTitles":
for name in data["SuffixTitles"] or []:
props.suffix_titles.add().name = name or ""
blenderbim.bim.helper.import_attributes("IfcPerson", props.person_attributes, person.get_info(), callback)
@classmethod
def clear_person(cls):
bpy.context.scene.BIMOwnerProperties.active_person_id = 0
@classmethod
def export_person_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
attributes = blenderbim.bim.helper.export_attributes(props.person_attributes)
attributes["MiddleNames"] = [v.name for v in props.middle_names] if props.middle_names else None
attributes["PrefixTitles"] = [v.name for v in props.prefix_titles] if props.prefix_titles else None
attributes["SuffixTitles"] = [v.name for v in props.suffix_titles] if props.suffix_titles else None
return attributes
@classmethod
def get_person(cls):
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_person_id)
@classmethod
def add_person_attribute(cls, name):
if name == "MiddleNames":
bpy.context.scene.BIMOwnerProperties.middle_names.add()
elif name == "PrefixTitles":
bpy.context.scene.BIMOwnerProperties.prefix_titles.add()
elif name == "SuffixTitles":
bpy.context.scene.BIMOwnerProperties.suffix_titles.add()
@classmethod
def remove_person_attribute(cls, name, id):
if name == "MiddleNames":
bpy.context.scene.BIMOwnerProperties.middle_names.remove(id)
elif name == "PrefixTitles":
bpy.context.scene.BIMOwnerProperties.prefix_titles.remove(id)
elif name == "SuffixTitles":
bpy.context.scene.BIMOwnerProperties.suffix_titles.remove(id)
@classmethod
def set_role(cls, role):
bpy.context.scene.BIMOwnerProperties.active_role_id = role.id()
@classmethod
def import_role_attributes(cls):
role = cls.get_role()
props = bpy.context.scene.BIMOwnerProperties
props.role_attributes.clear()
blenderbim.bim.helper.import_attributes("IfcActorRole", props.role_attributes, role.get_info())
@classmethod
def clear_role(cls):
bpy.context.scene.BIMOwnerProperties.active_role_id = 0
@classmethod
def get_role(cls):
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_role_id)
@classmethod
def export_role_attributes(cls):
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMOwnerProperties.role_attributes)
@@ -1,86 +0,0 @@
# 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 bpy
import ifcopenshell.api
import blenderbim.core.tool
import blenderbim.bim.helper
import blenderbim.tool as tool
class PersonEditor(blenderbim.core.tool.PersonEditor):
@classmethod
def set_person(cls, person):
bpy.context.scene.BIMOwnerProperties.active_person_id = person.id()
@classmethod
def import_attributes(cls):
person = tool.Ifc.get().by_id(bpy.context.scene.BIMOwnerProperties.active_person_id)
props = bpy.context.scene.BIMOwnerProperties
props.person_attributes.clear()
props.middle_names.clear()
props.prefix_titles.clear()
props.suffix_titles.clear()
def callback(name, prop, data):
if name == "MiddleNames":
for name in data["MiddleNames"] or []:
props.middle_names.add().name = name or ""
if name == "PrefixTitles":
for name in data["PrefixTitles"] or []:
props.prefix_titles.add().name = name or ""
if name == "SuffixTitles":
for name in data["SuffixTitles"] or []:
props.suffix_titles.add().name = name or ""
blenderbim.bim.helper.import_attributes("IfcPerson", props.person_attributes, person.get_info(), callback)
@classmethod
def clear_person(cls):
bpy.context.scene.BIMOwnerProperties.active_person_id = 0
@classmethod
def export_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
attributes = blenderbim.bim.helper.export_attributes(props.person_attributes)
attributes["MiddleNames"] = [v.name for v in props.middle_names] if props.middle_names else None
attributes["PrefixTitles"] = [v.name for v in props.prefix_titles] if props.prefix_titles else None
attributes["SuffixTitles"] = [v.name for v in props.suffix_titles] if props.suffix_titles else None
return attributes
@classmethod
def get_person(cls):
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_person_id)
@classmethod
def add_attribute(cls, name):
if name == "MiddleNames":
bpy.context.scene.BIMOwnerProperties.middle_names.add()
elif name == "PrefixTitles":
bpy.context.scene.BIMOwnerProperties.prefix_titles.add()
elif name == "SuffixTitles":
bpy.context.scene.BIMOwnerProperties.suffix_titles.add()
@classmethod
def remove_attribute(cls, name, id):
if name == "MiddleNames":
bpy.context.scene.BIMOwnerProperties.middle_names.remove(id)
elif name == "PrefixTitles":
bpy.context.scene.BIMOwnerProperties.prefix_titles.remove(id)
elif name == "SuffixTitles":
bpy.context.scene.BIMOwnerProperties.suffix_titles.remove(id)
+114
View File
@@ -0,0 +1,114 @@
# 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 ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
import blenderbim.bim.helper
class Style(blenderbim.core.tool.Style):
@classmethod
def disable_editing(cls, obj):
obj.BIMStyleProperties.is_editing = False
@classmethod
def enable_editing(cls, obj):
obj.BIMStyleProperties.is_editing = True
@classmethod
def export_surface_attributes(cls, obj):
return blenderbim.bim.helper.export_attributes(obj.BIMStyleProperties.attributes)
@classmethod
def get_context(cls, obj):
return ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
@classmethod
def get_name(cls, obj):
return obj.name
@classmethod
def get_style(cls, obj):
if obj.BIMMaterialProperties.ifc_style_id:
return tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id)
@classmethod
def get_surface_rendering_attributes(cls, obj):
transparency = obj.diffuse_color[3]
diffuse_colour = obj.diffuse_color
if obj.use_nodes and hasattr(obj.node_tree, "nodes") and "Principled BSDF" in obj.node_tree.nodes:
bsdf = obj.node_tree.nodes["Principled BSDF"]
transparency = bsdf.inputs["Alpha"].default_value
diffuse_colour = bsdf.inputs["Base Color"].default_value
transparency = 1 - transparency
return {
"SurfaceColour": {
"Name": None,
"Red": obj.diffuse_color[0],
"Green": obj.diffuse_color[1],
"Blue": obj.diffuse_color[2],
},
"Transparency": transparency,
"DiffuseColour": {
"Name": None,
"Red": obj.diffuse_color[0],
"Green": obj.diffuse_color[1],
"Blue": obj.diffuse_color[2],
},
}
@classmethod
def get_surface_rendering_style(cls, obj):
if obj.BIMMaterialProperties.ifc_style_id:
style = tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id)
items = [s for s in style.Styles if s.is_a("IfcSurfaceStyleRendering")]
if items:
return items[0]
@classmethod
def get_surface_shading_attributes(cls, obj):
return {
"SurfaceColour": {
"Name": None,
"Red": obj.diffuse_color[0],
"Green": obj.diffuse_color[1],
"Blue": obj.diffuse_color[2],
},
"Transparency": 1 - obj.diffuse_color[3],
}
@classmethod
def get_surface_shading_style(cls, obj):
if obj.BIMMaterialProperties.ifc_style_id:
style = tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id)
items = [s for s in style.Styles if s.is_a("IfcSurfaceStyleShading")]
if items:
return items[0]
@classmethod
def import_surface_attributes(cls, style, obj):
blenderbim.bim.helper.import_attributes2(style, obj.BIMStyleProperties.attributes)
@classmethod
def link(cls, style, obj):
obj.BIMMaterialProperties.ifc_style_id = style.id()
@classmethod
def unlink(self, obj):
obj.BIMMaterialProperties.ifc_style_id = 0
@@ -0,0 +1,59 @@
@style
Feature: Style
Scenario: Update style colours
Given an empty IFC project
And I add a cube
And I add a material
And I press "bim.add_style"
When I press "bim.update_style_colours"
Then nothing happens
Scenario: Remove style
Given an empty IFC project
And I add a cube
And I add a material
And I press "bim.add_style"
When I press "bim.remove_style"
Then nothing happens
Scenario: Add style
Given an empty IFC project
And I add a cube
And I add a material
When I press "bim.add_style"
Then nothing happens
Scenario: Unlink style
Given an empty IFC project
And I add a cube
And I add a material
And I press "bim.add_style"
When I press "bim.unlink_style"
Then nothing happens
Scenario: Enable editing style
Given an empty IFC project
And I add a cube
And I add a material
And I press "bim.add_style"
When I press "bim.enable_editing_style"
Then nothing happens
Scenario: Disable editing style
Given an empty IFC project
And I add a cube
And I add a material
And I press "bim.add_style"
And I press "bim.enable_editing_style"
When I press "bim.disable_editing_style"
Then nothing happens
Scenario: Edit style
Given an empty IFC project
And I add a cube
And I add a material
And I press "bim.add_style"
And I press "bim.enable_editing_style"
When I press "bim.edit_style"
Then nothing happens
+5
View File
@@ -49,6 +49,11 @@ def i_add_a_cube():
bpy.ops.mesh.primitive_cube_add() bpy.ops.mesh.primitive_cube_add()
@given("I add a material")
def i_add_a_material():
bpy.context.active_object.active_material = bpy.data.materials.new("Material")
@when(parsers.parse('I add a cube of size "{size}" at "{location}"')) @when(parsers.parse('I add a cube of size "{size}" at "{location}"'))
def i_add_a_cube_of_size_size_at_location(size, location): def i_add_a_cube_of_size_size_at_location(size, location):
bpy.ops.mesh.primitive_cube_add(size=float(size), location=[float(co) for co in location.split(",")]) bpy.ops.mesh.primitive_cube_add(size=float(size), location=[float(co) for co in location.split(",")])
+11 -32
View File
@@ -36,43 +36,15 @@ def blender():
@pytest.fixture @pytest.fixture
def aggregator(): def aggregate():
prophet = Prophecy(blenderbim.core.tool.Aggregator) prophet = Prophecy(blenderbim.core.tool.Aggregate)
yield prophet yield prophet
prophet.verify() prophet.verify()
@pytest.fixture @pytest.fixture
def person_editor(): def context():
prophet = Prophecy(blenderbim.core.tool.PersonEditor) prophet = Prophecy(blenderbim.core.tool.Context)
yield prophet
prophet.verify()
@pytest.fixture
def role_editor():
prophet = Prophecy(blenderbim.core.tool.RoleEditor)
yield prophet
prophet.verify()
@pytest.fixture
def address_editor():
prophet = Prophecy(blenderbim.core.tool.AddressEditor)
yield prophet
prophet.verify()
@pytest.fixture
def organisation_editor():
prophet = Prophecy(blenderbim.core.tool.OrganisationEditor)
yield prophet
prophet.verify()
@pytest.fixture
def context_editor():
prophet = Prophecy(blenderbim.core.tool.ContextEditor)
yield prophet yield prophet
prophet.verify() prophet.verify()
@@ -98,6 +70,13 @@ def selector():
prophet.verify() prophet.verify()
@pytest.fixture
def style():
prophet = Prophecy(blenderbim.core.tool.Style)
yield prophet
prophet.verify()
@pytest.fixture @pytest.fixture
def surveyor(): def surveyor():
prophet = Prophecy(blenderbim.core.tool.Surveyor) prophet = Prophecy(blenderbim.core.tool.Surveyor)
+29 -11
View File
@@ -1,32 +1,50 @@
# 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.aggregate as subject import blenderbim.core.aggregate as subject
from test.core.bootstrap import ifc, aggregator, collector from test.core.bootstrap import ifc, aggregate, collector
class TestEnableEditingAggregate: class TestEnableEditingAggregate:
def test_run(self, aggregator): def test_run(self, aggregate):
aggregator.enable_editing("obj").should_be_called() aggregate.enable_editing("obj").should_be_called()
subject.enable_editing_aggregate(aggregator, obj="obj") subject.enable_editing_aggregate(aggregate, obj="obj")
class TestDisableEditingAggregate: class TestDisableEditingAggregate:
def test_run(self, aggregator): def test_run(self, aggregate):
aggregator.disable_editing("obj").should_be_called() aggregate.disable_editing("obj").should_be_called()
subject.disable_editing_aggregate(aggregator, obj="obj") subject.disable_editing_aggregate(aggregate, obj="obj")
class TestAssignObject: class TestAssignObject:
def test_run(self, ifc, aggregator, collector): def test_run(self, ifc, aggregate, collector):
aggregator.can_aggregate("relating_obj", "related_obj").should_be_called().will_return(True) aggregate.can_aggregate("relating_obj", "related_obj").should_be_called().will_return(True)
ifc.get_entity("relating_obj").should_be_called().will_return("relating_object") ifc.get_entity("relating_obj").should_be_called().will_return("relating_object")
ifc.get_entity("related_obj").should_be_called().will_return("related_object") ifc.get_entity("related_obj").should_be_called().will_return("related_object")
ifc.run( ifc.run(
"aggregate.assign_object", product="related_object", relating_object="relating_object" "aggregate.assign_object", product="related_object", relating_object="relating_object"
).should_be_called().will_return("rel") ).should_be_called().will_return("rel")
aggregator.disable_editing("related_obj").should_be_called() aggregate.disable_editing("related_obj").should_be_called()
collector.assign("relating_obj").should_be_called() collector.assign("relating_obj").should_be_called()
collector.assign("related_obj").should_be_called() collector.assign("related_obj").should_be_called()
assert ( assert (
subject.assign_object(ifc, aggregator, collector, relating_obj="relating_obj", related_obj="related_obj") subject.assign_object(ifc, aggregate, collector, relating_obj="relating_obj", related_obj="related_obj")
== "rel" == "rel"
) )
+13 -13
View File
@@ -17,7 +17,7 @@
# 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 blenderbim.core.context as subject import blenderbim.core.context as subject
from test.core.bootstrap import ifc, context_editor from test.core.bootstrap import ifc, context
class TestAddContext: class TestAddContext:
@@ -53,22 +53,22 @@ class TestRemoveContext:
class TestEnableEditingContext: class TestEnableEditingContext:
def test_run(self, context_editor): def test_run(self, context):
context_editor.set_context("context").should_be_called() context.set_context("context").should_be_called()
context_editor.import_attributes().should_be_called() context.import_attributes().should_be_called()
subject.enable_editing_context(context_editor, context="context") subject.enable_editing_context(context, context="context")
class TestDisableEditingContext: class TestDisableEditingContext:
def test_run(self, context_editor): def test_run(self, context):
context_editor.clear_context().should_be_called() context.clear_context().should_be_called()
subject.disable_editing_context(context_editor) subject.disable_editing_context(context)
class TestEditContext: class TestEditContext:
def test_run(self, ifc, context_editor): def test_run(self, ifc, context):
context_editor.get_context().should_be_called().will_return("context") context.get_context().should_be_called().will_return("context")
context_editor.export_attributes().should_be_called().will_return("attributes") context.export_attributes().should_be_called().will_return("attributes")
ifc.run("context.edit_context", context="context", attributes="attributes").should_be_called() ifc.run("context.edit_context", context="context", attributes="attributes").should_be_called()
context_editor.clear_context().should_be_called() context.clear_context().should_be_called()
subject.edit_context(ifc, context_editor) subject.edit_context(ifc, context)
+61 -61
View File
@@ -18,7 +18,7 @@
import blenderbim.core.owner as subject import blenderbim.core.owner as subject
from test.core.bootstrap import ifc, blender, person_editor, role_editor, address_editor, organisation_editor, owner from test.core.bootstrap import ifc, owner
class TestAddPerson: class TestAddPerson:
@@ -34,37 +34,37 @@ class TestRemovePerson:
class TestEnableEditingPerson: class TestEnableEditingPerson:
def test_run(self, person_editor): def test_run(self, owner):
person_editor.set_person("person").should_be_called() owner.set_person("person").should_be_called()
person_editor.import_attributes().should_be_called() owner.import_person_attributes().should_be_called()
subject.enable_editing_person(person_editor, person="person") subject.enable_editing_person(owner, person="person")
class TestDisableEditingPerson: class TestDisableEditingPerson:
def test_run(self, person_editor): def test_run(self, owner):
person_editor.clear_person().should_be_called() owner.clear_person().should_be_called()
subject.disable_editing_person(person_editor) subject.disable_editing_person(owner)
class TestEditPerson: class TestEditPerson:
def test_run(self, ifc, person_editor): def test_run(self, ifc, owner):
person_editor.get_person().should_be_called().will_return("person") owner.get_person().should_be_called().will_return("person")
person_editor.export_attributes().should_be_called().will_return("attributes") owner.export_person_attributes().should_be_called().will_return("attributes")
ifc.run("owner.edit_person", person="person", attributes="attributes").should_be_called() ifc.run("owner.edit_person", person="person", attributes="attributes").should_be_called()
person_editor.clear_person().should_be_called() owner.clear_person().should_be_called()
subject.edit_person(ifc, person_editor) subject.edit_person(ifc, owner)
class TestAddPersonAttribute: class TestAddPersonAttribute:
def test_run(self, person_editor): def test_run(self, owner):
person_editor.add_attribute("name").should_be_called() owner.add_person_attribute("name").should_be_called()
subject.add_person_attribute(person_editor, name="name") subject.add_person_attribute(owner, name="name")
class TestRemovePersonAttribute: class TestRemovePersonAttribute:
def test_run(self, person_editor): def test_run(self, owner):
person_editor.remove_attribute("name", "id").should_be_called() owner.remove_person_attribute("name", "id").should_be_called()
subject.remove_person_attribute(person_editor, name="name", id="id") subject.remove_person_attribute(owner, name="name", id="id")
class TestAddRole: class TestAddRole:
@@ -80,25 +80,25 @@ class TestRemoveRole:
class TestEnableEditingRole: class TestEnableEditingRole:
def test_run(self, role_editor): def test_run(self, owner):
role_editor.set_role("role").should_be_called() owner.set_role("role").should_be_called()
role_editor.import_attributes().should_be_called() owner.import_role_attributes().should_be_called()
subject.enable_editing_role(role_editor, role="role") subject.enable_editing_role(owner, role="role")
class TestDisableEditingRole: class TestDisableEditingRole:
def test_run(self, role_editor): def test_run(self, owner):
role_editor.clear_role().should_be_called() owner.clear_role().should_be_called()
subject.disable_editing_role(role_editor) subject.disable_editing_role(owner)
class TestEditRole: class TestEditRole:
def test_run(self, ifc, role_editor): def test_run(self, ifc, owner):
role_editor.export_attributes().should_be_called().will_return("attributes") owner.export_role_attributes().should_be_called().will_return("attributes")
role_editor.get_role().should_be_called().will_return("role") owner.get_role().should_be_called().will_return("role")
ifc.run("owner.edit_role", role="role", attributes="attributes").should_be_called() ifc.run("owner.edit_role", role="role", attributes="attributes").should_be_called()
role_editor.clear_role().should_be_called() owner.clear_role().should_be_called()
subject.edit_role(ifc, role_editor) subject.edit_role(ifc, owner)
class TestAddAddress: class TestAddAddress:
@@ -116,37 +116,37 @@ class TestRemoveAddress:
class TestEnableEditingAddress: class TestEnableEditingAddress:
def test_run(self, address_editor): def test_run(self, owner):
address_editor.set_address("address").should_be_called() owner.set_address("address").should_be_called()
address_editor.import_attributes().should_be_called() owner.import_address_attributes().should_be_called()
subject.enable_editing_address(address_editor, address="address") subject.enable_editing_address(owner, address="address")
class TestDisableEditingAddress: class TestDisableEditingAddress:
def test_run(self, address_editor): def test_run(self, owner):
address_editor.clear_address().should_be_called() owner.clear_address().should_be_called()
subject.disable_editing_address(address_editor) subject.disable_editing_address(owner)
class TestEditAddress: class TestEditAddress:
def test_run(self, ifc, address_editor): def test_run(self, ifc, owner):
address_editor.get_address().should_be_called().will_return("address") owner.get_address().should_be_called().will_return("address")
address_editor.export_attributes().should_be_called().will_return("attributes") owner.export_address_attributes().should_be_called().will_return("attributes")
ifc.run("owner.edit_address", address="address", attributes="attributes").should_be_called() ifc.run("owner.edit_address", address="address", attributes="attributes").should_be_called()
address_editor.clear_address().should_be_called() owner.clear_address().should_be_called()
subject.edit_address(ifc, address_editor) subject.edit_address(ifc, owner)
class TestAddAddressAttribute: class TestAddAddressAttribute:
def test_run(self, address_editor): def test_run(self, owner):
address_editor.add_attribute("name").should_be_called() owner.add_address_attribute("name").should_be_called()
subject.add_address_attribute(address_editor, name="name") subject.add_address_attribute(owner, name="name")
class TestRemoveAddressAttribute: class TestRemoveAddressAttribute:
def test_run(self, address_editor): def test_run(self, owner):
address_editor.remove_attribute("name", "id").should_be_called() owner.remove_address_attribute("name", "id").should_be_called()
subject.remove_address_attribute(address_editor, name="name", id="id") subject.remove_address_attribute(owner, name="name", id="id")
class TestAddOrganisation: class TestAddOrganisation:
@@ -162,25 +162,25 @@ class TestRemoveOrganisation:
class TestEnableEditingOrganisation: class TestEnableEditingOrganisation:
def test_run(self, organisation_editor): def test_run(self, owner):
organisation_editor.set_organisation("organisation").should_be_called() owner.set_organisation("organisation").should_be_called()
organisation_editor.import_attributes().should_be_called() owner.import_organisation_attributes().should_be_called()
subject.enable_editing_organisation(organisation_editor, organisation="organisation") subject.enable_editing_organisation(owner, organisation="organisation")
class TestDisableEditingOrganisation: class TestDisableEditingOrganisation:
def test_run(self, organisation_editor): def test_run(self, owner):
organisation_editor.clear_organisation().should_be_called() owner.clear_organisation().should_be_called()
subject.disable_editing_organisation(organisation_editor) subject.disable_editing_organisation(owner)
class TestEditOrganisation: class TestEditOrganisation:
def test_run(self, ifc, organisation_editor): def test_run(self, ifc, owner):
organisation_editor.get_organisation().should_be_called().will_return("organisation") owner.get_organisation().should_be_called().will_return("organisation")
organisation_editor.export_attributes().should_be_called().will_return("attributes") owner.export_organisation_attributes().should_be_called().will_return("attributes")
ifc.run("owner.edit_organisation", organisation="organisation", attributes="attributes").should_be_called() ifc.run("owner.edit_organisation", organisation="organisation", attributes="attributes").should_be_called()
organisation_editor.clear_organisation().should_be_called() owner.clear_organisation().should_be_called()
subject.edit_organisation(ifc, organisation_editor) subject.edit_organisation(ifc, owner)
class TestAddPersonAndOrganisation: class TestAddPersonAndOrganisation:
+98
View File
@@ -0,0 +1,98 @@
# 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 as subject
from test.core.bootstrap import ifc, style
class TestAddStyle:
def test_it_adds_a_style_with_rendering_attributes(self, ifc, style):
style.get_name("obj").should_be_called().will_return("name")
ifc.run("style.add_style", name="name").should_be_called().will_return("style")
style.link("style", "obj").should_be_called()
style.get_surface_rendering_attributes("obj").should_be_called().will_return("attributes")
ifc.run(
"style.add_surface_style", style="style", ifc_class="IfcSurfaceStyleRendering", attributes="attributes"
).should_be_called()
ifc.get_entity("obj").should_be_called().will_return(None)
assert subject.add_style(ifc, style, obj="obj") == "style"
def test_adding_a_style_linked_to_a_material(self, ifc, style):
style.get_name("obj").should_be_called().will_return("name")
ifc.run("style.add_style", name="name").should_be_called().will_return("style")
style.link("style", "obj").should_be_called()
style.get_surface_rendering_attributes("obj").should_be_called().will_return("attributes")
ifc.run(
"style.add_surface_style", style="style", ifc_class="IfcSurfaceStyleRendering", attributes="attributes"
).should_be_called()
ifc.get_entity("obj").should_be_called().will_return("material")
style.get_context("obj").should_be_called().will_return("context")
ifc.run("style.assign_material_style", material="material", style="style", context="context").should_be_called()
assert subject.add_style(ifc, style, obj="obj") == "style"
class TestRemoveStyle:
def test_run(self, ifc, style):
style.get_style("obj").should_be_called().will_return("style")
ifc.run("style.remove_style", style="style").should_be_called()
style.unlink(obj="obj").should_be_called()
subject.remove_style(ifc, style, obj="obj")
class TestUpdateStyleColours:
def test_updating_rendering_colours_if_available(self, ifc, style):
style.get_surface_rendering_style("obj").should_be_called().will_return("style")
style.get_surface_rendering_attributes("obj").should_be_called().will_return("attributes")
ifc.run("style.edit_surface_style", style="style", attributes="attributes").should_be_called()
subject.update_style_colours(ifc, style, obj="obj")
def test_updating_shading_colours_as_a_fallback_if_available(self, ifc, style):
style.get_surface_rendering_style("obj").should_be_called().will_return(None)
style.get_surface_shading_style("obj").should_be_called().will_return("style")
style.get_surface_shading_attributes("obj").should_be_called().will_return("attributes")
ifc.run("style.edit_surface_style", style="style", attributes="attributes").should_be_called()
subject.update_style_colours(ifc, style, obj="obj")
class TestUnlinkStyle:
def test_run(self, style):
style.unlink("obj").should_be_called()
subject.unlink_style(style, obj="obj")
class TestEnableEditingStyle:
def test_run(self, style):
style.enable_editing("obj").should_be_called()
style.get_style("obj").should_be_called().will_return("style")
style.import_surface_attributes("style", "obj").should_be_called()
subject.enable_editing_style(style, obj="obj")
class TestDisableEditingStyle:
def test_run(self, style):
style.disable_editing("obj").should_be_called()
subject.disable_editing_style(style, obj="obj")
class TestEditStyle:
def test_run(self, ifc, style):
style.get_style("obj").should_be_called().will_return("style")
style.export_surface_attributes("obj").should_be_called().will_return("attributes")
ifc.run("style.edit_presentation_style", style="style", attributes="attributes").should_be_called()
style.disable_editing("obj").should_be_called()
subject.edit_style(ifc, style, obj="obj")
@@ -1,197 +0,0 @@
# 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 bpy
import ifcopenshell
import test.bim.bootstrap
import blenderbim.core.tool
import blenderbim.tool as tool
from blenderbim.tool.address_editor import AddressEditor as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.AddressEditor)
class TestSetAddress(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
address = ifc.createIfcPostalAddress()
subject().set_address(address)
assert bpy.context.scene.BIMOwnerProperties.active_address_id == address.id()
class TestImportAttributes(test.bim.bootstrap.NewFile):
def test_importing_a_postal_address(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
address = ifc.createIfcPostalAddress()
address.Purpose = "USERDEFINED"
address.Description = "Description"
address.UserDefinedPurpose = "UserDefinedPurpose"
address.InternalLocation = "InternalLocation"
address.AddressLines = ["Address", "Lines"]
address.PostalBox = "PostalBox"
address.Town = "Town"
address.Region = "Region"
address.PostalCode = "PostalCode"
address.Country = "Country"
subject().set_address(address)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.address_attributes.get("Purpose").enum_value == "USERDEFINED"
assert props.address_attributes.get("Description").string_value == "Description"
assert props.address_attributes.get("UserDefinedPurpose").string_value == "UserDefinedPurpose"
assert props.address_attributes.get("InternalLocation").string_value == "InternalLocation"
assert props.address_attributes.get("PostalBox").string_value == "PostalBox"
assert props.address_attributes.get("Town").string_value == "Town"
assert props.address_attributes.get("Region").string_value == "Region"
assert props.address_attributes.get("PostalCode").string_value == "PostalCode"
assert props.address_attributes.get("Country").string_value == "Country"
assert len(props.address_lines) == 2
assert props.address_lines[0].name == "Address"
assert props.address_lines[1].name == "Lines"
def test_importing_a_postal_address_twice(self):
self.test_importing_a_postal_address()
ifc = tool.Ifc().get()
address = ifc.createIfcPostalAddress()
address.Purpose = "OFFICE"
subject().set_address(address)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.address_attributes.get("Purpose").enum_value == "OFFICE"
assert len(props.address_lines) == 0
def test_importing_a_telecom_address(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
address = ifc.createIfcTelecomAddress()
address.Purpose = "USERDEFINED"
address.Description = "Description"
address.UserDefinedPurpose = "UserDefinedPurpose"
address.TelephoneNumbers = ["Telephone", "Numbers"]
address.FacsimileNumbers = ["Facsimile", "Numbers"]
address.PagerNumber = "PagerNumber"
address.ElectronicMailAddresses = ["Electronic", "Mail", "Addresses"]
address.WWWHomePageURL = "WWWHomePageURL"
address.MessagingIDs = ["Messaging", "IDs"]
subject().set_address(address)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.address_attributes.get("Purpose").enum_value == "USERDEFINED"
assert props.address_attributes.get("Description").string_value == "Description"
assert props.address_attributes.get("UserDefinedPurpose").string_value == "UserDefinedPurpose"
assert [a.name for a in props.telephone_numbers] == ["Telephone", "Numbers"]
assert [a.name for a in props.facsimile_numbers] == ["Facsimile", "Numbers"]
assert [a.name for a in props.electronic_mail_addresses] == ["Electronic", "Mail", "Addresses"]
assert [a.name for a in props.messaging_ids] == ["Messaging", "IDs"]
def test_importing_a_telecom_address_twice(self):
self.test_importing_a_telecom_address()
ifc = tool.Ifc().get()
address = ifc.createIfcTelecomAddress()
address.Purpose = "OFFICE"
subject().set_address(address)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.address_attributes.get("Purpose").enum_value == "OFFICE"
assert len(props.telephone_numbers) == 0
assert len(props.facsimile_numbers) == 0
assert len(props.electronic_mail_addresses) == 0
assert len(props.messaging_ids) == 0
class TestClearAddress(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
address = ifc.createIfcPostalAddress()
subject().set_address(address)
subject().clear_address()
assert bpy.context.scene.BIMOwnerProperties.active_address_id == 0
class TestGetAddress(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
address = ifc.createIfcPostalAddress()
subject().set_address(address)
assert subject().get_address() == address
class TestExportAttributes(test.bim.bootstrap.NewFile):
def test_exporting_a_postal_address(self):
TestImportAttributes().test_importing_a_postal_address()
assert subject().export_attributes() == {
"Purpose": "USERDEFINED",
"Description": "Description",
"UserDefinedPurpose": "UserDefinedPurpose",
"InternalLocation": "InternalLocation",
"AddressLines": ["Address", "Lines"],
"PostalBox": "PostalBox",
"Town": "Town",
"Region": "Region",
"PostalCode": "PostalCode",
"Country": "Country",
}
def test_exporting_a_telecom_address(self):
TestImportAttributes().test_importing_a_telecom_address()
assert subject().export_attributes() == {
"Purpose": "USERDEFINED",
"Description": "Description",
"UserDefinedPurpose": "UserDefinedPurpose",
"TelephoneNumbers": ["Telephone", "Numbers"],
"FacsimileNumbers": ["Facsimile", "Numbers"],
"PagerNumber": "PagerNumber",
"ElectronicMailAddresses": ["Electronic", "Mail", "Addresses"],
"WWWHomePageURL": "WWWHomePageURL",
"MessagingIDs": ["Messaging", "IDs"],
}
class TestAddAttribute(test.bim.bootstrap.NewFile):
def test_run(self):
subject().add_attribute("AddressLines")
subject().add_attribute("TelephoneNumbers")
subject().add_attribute("FacsimileNumbers")
subject().add_attribute("ElectronicMailAddresses")
subject().add_attribute("MessagingIDs")
props = bpy.context.scene.BIMOwnerProperties
assert len(props.address_lines) == 1
assert len(props.telephone_numbers) == 1
assert len(props.facsimile_numbers) == 1
assert len(props.electronic_mail_addresses) == 1
assert len(props.messaging_ids) == 1
class TestRemoveAddress(test.bim.bootstrap.NewFile):
TestAddAttribute().test_run()
subject().remove_attribute("AddressLines", 0)
subject().remove_attribute("TelephoneNumbers", 0)
subject().remove_attribute("FacsimileNumbers", 0)
subject().remove_attribute("ElectronicMailAddresses", 0)
subject().remove_attribute("MessagingIDs", 0)
props = bpy.context.scene.BIMOwnerProperties
assert len(props.address_lines) == 0
assert len(props.telephone_numbers) == 0
assert len(props.facsimile_numbers) == 0
assert len(props.electronic_mail_addresses) == 0
assert len(props.messaging_ids) == 0
@@ -1,14 +1,32 @@
# 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 bpy import bpy
import ifcopenshell import ifcopenshell
import blenderbim.core.tool import blenderbim.core.tool
import blenderbim.tool as tool import blenderbim.tool as tool
from test.bim.bootstrap import NewFile from test.bim.bootstrap import NewFile
from blenderbim.tool.aggregator import Aggregator as subject from blenderbim.tool.aggregate import Aggregate as subject
class TestImplementsTool(NewFile): class TestImplementsTool(NewFile):
def test_run(self): def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Aggregator) assert isinstance(subject(), blenderbim.core.tool.Aggregate)
class TestEnableEditing(NewFile): class TestEnableEditing(NewFile):
@@ -21,12 +21,12 @@ import ifcopenshell
import test.bim.bootstrap import test.bim.bootstrap
import blenderbim.core.tool import blenderbim.core.tool
import blenderbim.tool as tool import blenderbim.tool as tool
from blenderbim.tool.context_editor import ContextEditor as subject from blenderbim.tool.context import Context as subject
class TestImplementsTool(test.bim.bootstrap.NewFile): class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self): def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.ContextEditor) assert isinstance(subject(), blenderbim.core.tool.Context)
class TestSetContext(test.bim.bootstrap.NewFile): class TestSetContext(test.bim.bootstrap.NewFile):
@@ -1,94 +0,0 @@
# 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 bpy
import ifcopenshell
import test.bim.bootstrap
import blenderbim.core.tool
import blenderbim.tool as tool
from blenderbim.tool.organisation_editor import OrganisationEditor as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.OrganisationEditor)
class TestSetOrganisation(test.bim.bootstrap.NewFile):
def test_run(self):
organisation = ifcopenshell.file().createIfcOrganization()
subject().set_organisation(organisation)
assert bpy.context.scene.BIMOwnerProperties.active_organisation_id == organisation.id()
class TestImportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
organisation = ifc.createIfcOrganization()
organisation.Identification = "Identification"
organisation.Name = "Name"
organisation.Description = "Description"
subject().set_organisation(organisation)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.organisation_attributes.get("Identification").string_value == "Identification"
assert props.organisation_attributes.get("Name").string_value == "Name"
assert props.organisation_attributes.get("Description").string_value == "Description"
def test_overwriting_a_previous_import(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
organisation = ifc.createIfcOrganization()
organisation.Identification = "Identification"
organisation.Description = "Description"
subject().set_organisation(organisation)
subject().import_attributes()
organisation.Identification = "Identification2"
organisation.Description = None
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.organisation_attributes.get("Identification").string_value == "Identification2"
assert props.organisation_attributes.get("Description").string_value == ""
class TestClearOrganisation(test.bim.bootstrap.NewFile):
def test_run(self):
props = bpy.context.scene.BIMOwnerProperties
props.active_organisation_id = 1
subject().clear_organisation()
assert props.active_organisation_id == 0
class TestExportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
TestImportAttributes().test_run()
assert subject().export_attributes() == {
"Identification": "Identification",
"Name": "Name",
"Description": "Description",
}
class TestGetOrganisation(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
organisation = ifc.createIfcOrganization()
subject().set_organisation(organisation)
assert subject().get_organisation() == organisation
+425
View File
@@ -51,3 +51,428 @@ class TestClearUser(test.bim.bootstrap.NewFile):
TestSetUser().test_run() TestSetUser().test_run()
subject.clear_user() subject.clear_user()
assert bpy.context.scene.BIMOwnerProperties.active_user_id == 0 assert bpy.context.scene.BIMOwnerProperties.active_user_id == 0
class TestSetAddress(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
address = ifc.createIfcPostalAddress()
subject().set_address(address)
assert bpy.context.scene.BIMOwnerProperties.active_address_id == address.id()
class TestImportAddressAttributes(test.bim.bootstrap.NewFile):
def test_importing_a_postal_address(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
address = ifc.createIfcPostalAddress()
address.Purpose = "USERDEFINED"
address.Description = "Description"
address.UserDefinedPurpose = "UserDefinedPurpose"
address.InternalLocation = "InternalLocation"
address.AddressLines = ["Address", "Lines"]
address.PostalBox = "PostalBox"
address.Town = "Town"
address.Region = "Region"
address.PostalCode = "PostalCode"
address.Country = "Country"
subject().set_address(address)
subject().import_address_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.address_attributes.get("Purpose").enum_value == "USERDEFINED"
assert props.address_attributes.get("Description").string_value == "Description"
assert props.address_attributes.get("UserDefinedPurpose").string_value == "UserDefinedPurpose"
assert props.address_attributes.get("InternalLocation").string_value == "InternalLocation"
assert props.address_attributes.get("PostalBox").string_value == "PostalBox"
assert props.address_attributes.get("Town").string_value == "Town"
assert props.address_attributes.get("Region").string_value == "Region"
assert props.address_attributes.get("PostalCode").string_value == "PostalCode"
assert props.address_attributes.get("Country").string_value == "Country"
assert len(props.address_lines) == 2
assert props.address_lines[0].name == "Address"
assert props.address_lines[1].name == "Lines"
def test_importing_a_postal_address_twice(self):
self.test_importing_a_postal_address()
ifc = tool.Ifc().get()
address = ifc.createIfcPostalAddress()
address.Purpose = "OFFICE"
subject().set_address(address)
subject().import_address_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.address_attributes.get("Purpose").enum_value == "OFFICE"
assert len(props.address_lines) == 0
def test_importing_a_telecom_address(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
address = ifc.createIfcTelecomAddress()
address.Purpose = "USERDEFINED"
address.Description = "Description"
address.UserDefinedPurpose = "UserDefinedPurpose"
address.TelephoneNumbers = ["Telephone", "Numbers"]
address.FacsimileNumbers = ["Facsimile", "Numbers"]
address.PagerNumber = "PagerNumber"
address.ElectronicMailAddresses = ["Electronic", "Mail", "Addresses"]
address.WWWHomePageURL = "WWWHomePageURL"
address.MessagingIDs = ["Messaging", "IDs"]
subject().set_address(address)
subject().import_address_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.address_attributes.get("Purpose").enum_value == "USERDEFINED"
assert props.address_attributes.get("Description").string_value == "Description"
assert props.address_attributes.get("UserDefinedPurpose").string_value == "UserDefinedPurpose"
assert [a.name for a in props.telephone_numbers] == ["Telephone", "Numbers"]
assert [a.name for a in props.facsimile_numbers] == ["Facsimile", "Numbers"]
assert [a.name for a in props.electronic_mail_addresses] == ["Electronic", "Mail", "Addresses"]
assert [a.name for a in props.messaging_ids] == ["Messaging", "IDs"]
def test_importing_a_telecom_address_twice(self):
self.test_importing_a_telecom_address()
ifc = tool.Ifc().get()
address = ifc.createIfcTelecomAddress()
address.Purpose = "OFFICE"
subject().set_address(address)
subject().import_address_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.address_attributes.get("Purpose").enum_value == "OFFICE"
assert len(props.telephone_numbers) == 0
assert len(props.facsimile_numbers) == 0
assert len(props.electronic_mail_addresses) == 0
assert len(props.messaging_ids) == 0
class TestClearAddress(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
address = ifc.createIfcPostalAddress()
subject().set_address(address)
subject().clear_address()
assert bpy.context.scene.BIMOwnerProperties.active_address_id == 0
class TestGetAddress(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
address = ifc.createIfcPostalAddress()
subject().set_address(address)
assert subject().get_address() == address
class TestExportAttributes(test.bim.bootstrap.NewFile):
def test_exporting_a_postal_address(self):
TestImportAddressAttributes().test_importing_a_postal_address()
assert subject().export_address_attributes() == {
"Purpose": "USERDEFINED",
"Description": "Description",
"UserDefinedPurpose": "UserDefinedPurpose",
"InternalLocation": "InternalLocation",
"AddressLines": ["Address", "Lines"],
"PostalBox": "PostalBox",
"Town": "Town",
"Region": "Region",
"PostalCode": "PostalCode",
"Country": "Country",
}
def test_exporting_a_telecom_address(self):
TestImportAddressAttributes().test_importing_a_telecom_address()
assert subject().export_address_attributes() == {
"Purpose": "USERDEFINED",
"Description": "Description",
"UserDefinedPurpose": "UserDefinedPurpose",
"TelephoneNumbers": ["Telephone", "Numbers"],
"FacsimileNumbers": ["Facsimile", "Numbers"],
"PagerNumber": "PagerNumber",
"ElectronicMailAddresses": ["Electronic", "Mail", "Addresses"],
"WWWHomePageURL": "WWWHomePageURL",
"MessagingIDs": ["Messaging", "IDs"],
}
class TestAddAddressAttribute(test.bim.bootstrap.NewFile):
def test_run(self):
subject().add_address_attribute("AddressLines")
subject().add_address_attribute("TelephoneNumbers")
subject().add_address_attribute("FacsimileNumbers")
subject().add_address_attribute("ElectronicMailAddresses")
subject().add_address_attribute("MessagingIDs")
props = bpy.context.scene.BIMOwnerProperties
assert len(props.address_lines) == 1
assert len(props.telephone_numbers) == 1
assert len(props.facsimile_numbers) == 1
assert len(props.electronic_mail_addresses) == 1
assert len(props.messaging_ids) == 1
class TestRemoveAddressAttribute(test.bim.bootstrap.NewFile):
TestAddAddressAttribute().test_run()
subject().remove_address_attribute("AddressLines", 0)
subject().remove_address_attribute("TelephoneNumbers", 0)
subject().remove_address_attribute("FacsimileNumbers", 0)
subject().remove_address_attribute("ElectronicMailAddresses", 0)
subject().remove_address_attribute("MessagingIDs", 0)
props = bpy.context.scene.BIMOwnerProperties
assert len(props.address_lines) == 0
assert len(props.telephone_numbers) == 0
assert len(props.facsimile_numbers) == 0
assert len(props.electronic_mail_addresses) == 0
assert len(props.messaging_ids) == 0
class TestSetOrganisation(test.bim.bootstrap.NewFile):
def test_run(self):
organisation = ifcopenshell.file().createIfcOrganization()
subject().set_organisation(organisation)
assert bpy.context.scene.BIMOwnerProperties.active_organisation_id == organisation.id()
class TestImportOrganisationAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
organisation = ifc.createIfcOrganization()
organisation.Identification = "Identification"
organisation.Name = "Name"
organisation.Description = "Description"
subject().set_organisation(organisation)
subject().import_organisation_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.organisation_attributes.get("Identification").string_value == "Identification"
assert props.organisation_attributes.get("Name").string_value == "Name"
assert props.organisation_attributes.get("Description").string_value == "Description"
def test_overwriting_a_previous_import(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
organisation = ifc.createIfcOrganization()
organisation.Identification = "Identification"
organisation.Description = "Description"
subject().set_organisation(organisation)
subject().import_organisation_attributes()
organisation.Identification = "Identification2"
organisation.Description = None
subject().import_organisation_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.organisation_attributes.get("Identification").string_value == "Identification2"
assert props.organisation_attributes.get("Description").string_value == ""
class TestClearOrganisation(test.bim.bootstrap.NewFile):
def test_run(self):
props = bpy.context.scene.BIMOwnerProperties
props.active_organisation_id = 1
subject().clear_organisation()
assert props.active_organisation_id == 0
class TestExportOrganisationAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
TestImportOrganisationAttributes().test_run()
assert subject().export_organisation_attributes() == {
"Identification": "Identification",
"Name": "Name",
"Description": "Description",
}
class TestGetOrganisation(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
organisation = ifc.createIfcOrganization()
subject().set_organisation(organisation)
assert subject().get_organisation() == organisation
class TestSetPerson(test.bim.bootstrap.NewFile):
def test_run(self):
person = ifcopenshell.file().createIfcPerson()
subject().set_person(person)
assert bpy.context.scene.BIMOwnerProperties.active_person_id == person.id()
class TestImportPersonAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
person = ifc.createIfcPerson()
person.Identification = "identification"
person.GivenName = "given_name"
person.FamilyName = "family_name"
person.MiddleNames = ("middle", "names")
person.PrefixTitles = ("prefix", "titles")
person.SuffixTitles = ("suffix", "titles")
subject().set_person(person)
subject().import_person_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.person_attributes.get("Identification").string_value == "identification"
assert props.person_attributes.get("GivenName").string_value == "given_name"
assert props.person_attributes.get("FamilyName").string_value == "family_name"
assert len(props.middle_names) == 2
assert props.middle_names[0].name == "middle"
assert props.middle_names[1].name == "names"
assert len(props.prefix_titles) == 2
assert props.prefix_titles[0].name == "prefix"
assert props.prefix_titles[1].name == "titles"
assert len(props.suffix_titles) == 2
assert props.suffix_titles[0].name == "suffix"
assert props.suffix_titles[1].name == "titles"
def test_overwriting_a_previous_import(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
person = ifc.createIfcPerson()
person.Identification = "identification"
person.GivenName = "given_name"
subject().set_person(person)
subject().import_person_attributes()
person.Identification = "identification2"
person.GivenName = None
subject().import_person_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.person_attributes.get("Identification").string_value == "identification2"
assert props.person_attributes.get("GivenName").string_value == ""
class TestClearPerson(test.bim.bootstrap.NewFile):
def test_run(self):
props = bpy.context.scene.BIMOwnerProperties
props.active_person_id = 1
subject().clear_person()
assert props.active_person_id == 0
class TestExportPersonAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
person = ifc.createIfcPerson()
person.Identification = "identification"
person.GivenName = "given_name"
person.FamilyName = "family_name"
person.MiddleNames = ("middle", "names")
person.PrefixTitles = ("prefix", "titles")
person.SuffixTitles = ("suffix", "titles")
subject().set_person(person)
subject().import_person_attributes()
assert subject().export_person_attributes() == {
"Identification": "identification",
"GivenName": "given_name",
"FamilyName": "family_name",
"MiddleNames": ["middle", "names"],
"PrefixTitles": ["prefix", "titles"],
"SuffixTitles": ["suffix", "titles"],
}
def test_getting_empty_list_attributes_as_none(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
result = subject().export_person_attributes()
assert result["MiddleNames"] is None
assert result["PrefixTitles"] is None
assert result["SuffixTitles"] is None
class TestGetPerson(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
person = ifc.createIfcPerson()
subject().set_person(person)
assert subject().get_person() == person
class TestAddPersonAttribute(test.bim.bootstrap.NewFile):
def test_run(self):
subject().add_person_attribute("MiddleNames")
subject().add_person_attribute("PrefixTitles")
subject().add_person_attribute("SuffixTitles")
props = bpy.context.scene.BIMOwnerProperties
assert len(props.middle_names) == 1
assert len(props.prefix_titles) == 1
assert len(props.suffix_titles) == 1
class TestRemovePersonAttribute(test.bim.bootstrap.NewFile):
def test_run(self):
subject().add_person_attribute("MiddleNames")
subject().remove_person_attribute("MiddleNames", 0)
subject().add_person_attribute("PrefixTitles")
subject().remove_person_attribute("PrefixTitles", 0)
subject().add_person_attribute("SuffixTitles")
subject().remove_person_attribute("SuffixTitles", 0)
props = bpy.context.scene.BIMOwnerProperties
assert len(props.middle_names) == 0
assert len(props.prefix_titles) == 0
assert len(props.suffix_titles) == 0
class TestSetRole(test.bim.bootstrap.NewFile):
def test_run(self):
role = ifcopenshell.file().createIfcActorRole()
subject().set_role(role)
assert bpy.context.scene.BIMOwnerProperties.active_role_id == role.id()
class TestImportRoleAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
role = ifc.createIfcActorRole()
role.Role = "USERDEFINED"
role.UserDefinedRole = "UserDefinedRole"
role.Description = "Description"
subject().set_role(role)
subject().import_role_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.role_attributes.get("Role").enum_value == "USERDEFINED"
assert props.role_attributes.get("UserDefinedRole").string_value == "UserDefinedRole"
assert props.role_attributes.get("Description").string_value == "Description"
def test_importing_twice(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
role = ifc.createIfcActorRole()
role.Role = "USERDEFINED"
subject().set_role(role)
subject().import_role_attributes()
role.Role = "ARCHITECT"
subject().import_role_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.role_attributes.get("Role").enum_value == "ARCHITECT"
class TestClearRole(test.bim.bootstrap.NewFile):
def test_run(self):
role = ifcopenshell.file().createIfcActorRole()
subject().set_role(role)
subject().clear_role()
assert bpy.context.scene.BIMOwnerProperties.active_role_id == 0
class TestGetRole(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
role = ifc.createIfcActorRole()
subject().set_role(role)
assert subject().get_role() == role
class TestExportRoleAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
role = ifc.createIfcActorRole()
role.Role = "USERDEFINED"
role.UserDefinedRole = "UserDefinedRole"
role.Description = "Description"
subject().set_role(role)
subject().import_role_attributes()
assert subject().export_role_attributes() == {
"Role": "USERDEFINED",
"UserDefinedRole": "UserDefinedRole",
"Description": "Description",
}
@@ -1,152 +0,0 @@
# 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 bpy
import ifcopenshell
import test.bim.bootstrap
import blenderbim.core.tool
import blenderbim.tool as tool
from blenderbim.tool.person_editor import PersonEditor as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.PersonEditor)
class TestSetPerson(test.bim.bootstrap.NewFile):
def test_run(self):
person = ifcopenshell.file().createIfcPerson()
subject().set_person(person)
assert bpy.context.scene.BIMOwnerProperties.active_person_id == person.id()
class TestImportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
person = ifc.createIfcPerson()
person.Identification = "identification"
person.GivenName = "given_name"
person.FamilyName = "family_name"
person.MiddleNames = ("middle", "names")
person.PrefixTitles = ("prefix", "titles")
person.SuffixTitles = ("suffix", "titles")
subject().set_person(person)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.person_attributes.get("Identification").string_value == "identification"
assert props.person_attributes.get("GivenName").string_value == "given_name"
assert props.person_attributes.get("FamilyName").string_value == "family_name"
assert len(props.middle_names) == 2
assert props.middle_names[0].name == "middle"
assert props.middle_names[1].name == "names"
assert len(props.prefix_titles) == 2
assert props.prefix_titles[0].name == "prefix"
assert props.prefix_titles[1].name == "titles"
assert len(props.suffix_titles) == 2
assert props.suffix_titles[0].name == "suffix"
assert props.suffix_titles[1].name == "titles"
def test_overwriting_a_previous_import(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
person = ifc.createIfcPerson()
person.Identification = "identification"
person.GivenName = "given_name"
subject().set_person(person)
subject().import_attributes()
person.Identification = "identification2"
person.GivenName = None
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.person_attributes.get("Identification").string_value == "identification2"
assert props.person_attributes.get("GivenName").string_value == ""
class TestClearPerson(test.bim.bootstrap.NewFile):
def test_run(self):
props = bpy.context.scene.BIMOwnerProperties
props.active_person_id = 1
subject().clear_person()
assert props.active_person_id == 0
class TestExportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
person = ifc.createIfcPerson()
person.Identification = "identification"
person.GivenName = "given_name"
person.FamilyName = "family_name"
person.MiddleNames = ("middle", "names")
person.PrefixTitles = ("prefix", "titles")
person.SuffixTitles = ("suffix", "titles")
subject().set_person(person)
subject().import_attributes()
assert subject().export_attributes() == {
"Identification": "identification",
"GivenName": "given_name",
"FamilyName": "family_name",
"MiddleNames": ["middle", "names"],
"PrefixTitles": ["prefix", "titles"],
"SuffixTitles": ["suffix", "titles"],
}
def test_getting_empty_list_attributes_as_none(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
result = subject().export_attributes()
assert result["MiddleNames"] is None
assert result["PrefixTitles"] is None
assert result["SuffixTitles"] is None
class TestGetPerson(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
person = ifc.createIfcPerson()
subject().set_person(person)
assert subject().get_person() == person
class TestAddAttribute(test.bim.bootstrap.NewFile):
def test_run(self):
subject().add_attribute("MiddleNames")
subject().add_attribute("PrefixTitles")
subject().add_attribute("SuffixTitles")
props = bpy.context.scene.BIMOwnerProperties
assert len(props.middle_names) == 1
assert len(props.prefix_titles) == 1
assert len(props.suffix_titles) == 1
class TestRemoveAttribute(test.bim.bootstrap.NewFile):
def test_run(self):
subject().add_attribute("MiddleNames")
subject().remove_attribute("MiddleNames", 0)
subject().add_attribute("PrefixTitles")
subject().remove_attribute("PrefixTitles", 0)
subject().add_attribute("SuffixTitles")
subject().remove_attribute("SuffixTitles", 0)
props = bpy.context.scene.BIMOwnerProperties
assert len(props.middle_names) == 0
assert len(props.prefix_titles) == 0
assert len(props.suffix_titles) == 0
@@ -1,98 +0,0 @@
# 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 bpy
import ifcopenshell
import test.bim.bootstrap
import blenderbim.core.tool
import blenderbim.tool as tool
from blenderbim.tool.role_editor import RoleEditor as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.RoleEditor)
class TestSetRole(test.bim.bootstrap.NewFile):
def test_run(self):
role = ifcopenshell.file().createIfcActorRole()
subject().set_role(role)
assert bpy.context.scene.BIMOwnerProperties.active_role_id == role.id()
class TestImportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
role = ifc.createIfcActorRole()
role.Role = "USERDEFINED"
role.UserDefinedRole = "UserDefinedRole"
role.Description = "Description"
subject().set_role(role)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.role_attributes.get("Role").enum_value == "USERDEFINED"
assert props.role_attributes.get("UserDefinedRole").string_value == "UserDefinedRole"
assert props.role_attributes.get("Description").string_value == "Description"
def test_importing_twice(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
role = ifc.createIfcActorRole()
role.Role = "USERDEFINED"
subject().set_role(role)
subject().import_attributes()
role.Role = "ARCHITECT"
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.role_attributes.get("Role").enum_value == "ARCHITECT"
class TestClearRole(test.bim.bootstrap.NewFile):
def test_run(self):
role = ifcopenshell.file().createIfcActorRole()
subject().set_role(role)
subject().clear_role()
assert bpy.context.scene.BIMOwnerProperties.active_role_id == 0
class TestGetRole(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
role = ifc.createIfcActorRole()
subject().set_role(role)
assert subject().get_role() == role
class TestExportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
role = ifc.createIfcActorRole()
role.Role = "USERDEFINED"
role.UserDefinedRole = "UserDefinedRole"
role.Description = "Description"
subject().set_role(role)
subject().import_attributes()
assert subject().export_attributes() == {
"Role": "USERDEFINED",
"UserDefinedRole": "UserDefinedRole",
"Description": "Description",
}
+183
View File
@@ -0,0 +1,183 @@
# 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 bpy
import ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.style import Style as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Style)
class TestDisableEditing(NewFile):
def test_run(self):
obj = bpy.data.materials.new("Material")
obj.BIMStyleProperties.is_editing = True
subject.disable_editing(obj)
assert obj.BIMStyleProperties.is_editing is False
class TestEnableEditing(NewFile):
def test_run(self):
obj = bpy.data.materials.new("Material")
subject.enable_editing(obj)
assert obj.BIMStyleProperties.is_editing is True
class TestExportSurfaceAttributes(NewFile):
def test_run(self):
TestImportSurfaceAttributes().test_run()
obj = bpy.data.materials.get("Material")
assert subject.export_surface_attributes(obj) == {"Name": "Name", "Side": "BOTH"}
class TestGetContext(NewFile):
def test_run(self):
bpy.ops.bim.create_project()
context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
assert subject.get_context("obj") == context
class TestGetName(NewFile):
def test_run(self):
assert subject.get_name(bpy.data.materials.new("Material")) == "Material"
class TestGetStyle(NewFile):
def test_getting_no_style(self):
assert subject.get_style(bpy.data.materials.new("Material")) is None
def test_getting_a_linked_style(self):
tool.Ifc.set(ifcopenshell.file())
style = tool.Ifc.get().createIfcSurfaceStyle()
obj = bpy.data.materials.new("Material")
obj.BIMMaterialProperties.ifc_style_id = style.id()
assert subject.get_style(obj) == style
class TestGetSurfaceRenderingAttributes(NewFile):
def test_get_colours_from_a_basic_material(self):
obj = bpy.data.materials.new("Material")
obj.diffuse_color = [1, 1, 1, 1]
assert subject.get_surface_rendering_attributes(obj) == {
"SurfaceColour": {
"Name": None,
"Red": 1,
"Green": 1,
"Blue": 1,
},
"Transparency": 0,
"DiffuseColour": {
"Name": None,
"Red": 1,
"Green": 1,
"Blue": 1,
},
}
def test_get_different_surface_and_diffuse_colours_from_a_node_based_material(self):
obj = bpy.data.materials.new("Material")
obj.diffuse_color = [1, 1, 1, 1]
obj.use_nodes = True
node = obj.node_tree.nodes["Principled BSDF"]
node.inputs["Alpha"].default_value = 0.8
node.inputs["Base Color"].default_value = [0.5, 0.5, 0.5, 0.5]
assert subject.get_surface_rendering_attributes(obj) == {
"SurfaceColour": {
"Name": None,
"Red": 1,
"Green": 1,
"Blue": 1,
},
"Transparency": 1 - node.inputs["Alpha"].default_value,
"DiffuseColour": {
"Name": None,
"Red": 1,
"Green": 1,
"Blue": 1,
},
}
class TestGetSurfaceRenderingStyle(NewFile):
def test_run(self):
tool.Ifc.set(ifcopenshell.file())
style_item = tool.Ifc.get().createIfcSurfaceStyleRendering()
style = tool.Ifc.get().createIfcSurfaceStyle(Styles=[style_item])
obj = bpy.data.materials.new("Material")
obj.BIMMaterialProperties.ifc_style_id = style.id()
assert subject.get_surface_rendering_style(obj) == style_item
class TestGetSurfaceShadingAttributes(NewFile):
def test_get_colours_from_a_basic_material(self):
obj = bpy.data.materials.new("Material")
obj.diffuse_color = [1, 1, 1, 1]
assert subject.get_surface_shading_attributes(obj) == {
"SurfaceColour": {
"Name": None,
"Red": 1,
"Green": 1,
"Blue": 1,
},
"Transparency": 0,
}
class TestGetSurfaceShadingStyle(NewFile):
def test_run(self):
tool.Ifc.set(ifcopenshell.file())
style_item = tool.Ifc.get().createIfcSurfaceStyleShading()
style = tool.Ifc.get().createIfcSurfaceStyle(Styles=[style_item])
obj = bpy.data.materials.new("Material")
obj.BIMMaterialProperties.ifc_style_id = style.id()
assert subject.get_surface_shading_style(obj) == style_item
class TestImportSurfaceAttributes(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
style = ifc.createIfcSurfaceStyle("Name", "BOTH")
obj = bpy.data.materials.new("Material")
subject.import_surface_attributes(style, obj)
assert obj.BIMStyleProperties.attributes.get("Name").string_value == "Name"
assert obj.BIMStyleProperties.attributes.get("Side").enum_value == "BOTH"
class TestLink(NewFile):
def test_run(self):
obj = bpy.data.materials.new("Material")
ifc = ifcopenshell.file()
style = ifc.createIfcSurfaceStyle()
subject.link(style, obj)
assert obj.BIMMaterialProperties.ifc_style_id == style.id()
class TestUnlink(NewFile):
def test_run(self):
obj = bpy.data.materials.new("Material")
ifc = ifcopenshell.file()
style = ifc.createIfcSurfaceStyle()
subject.link(style, obj)
subject.unlink(obj)
assert obj.BIMMaterialProperties.ifc_style_id == 0
@@ -1,43 +1,10 @@
class Usecase: class Usecase:
def __init__(self, file, **settings): def __init__(self, file, **settings):
self.file = file self.file = file
self.settings = { self.settings = {"name": None}
"name": "Name",
"surface_colour": [], # RGB
"diffuse_colour": [], # RGB
"transparency": 0,
"external_definition": {"location": None, "identification": None, "name": "Name"},
}
for key, value in settings.items(): for key, value in settings.items():
self.settings[key] = value self.settings[key] = value
def execute(self): def execute(self):
styles = [self.create_surface_style_rendering()]
if self.settings["external_definition"]:
styles.append(self.create_externally_defined_surface_style())
# Name is filled out because Revit treats this incorrectly as the material name # Name is filled out because Revit treats this incorrectly as the material name
return self.file.createIfcSurfaceStyle(self.settings["name"], "BOTH", styles) return self.file.createIfcSurfaceStyle(self.settings["name"], "BOTH")
def create_surface_style_rendering(self):
return self.file.create_entity(
"IfcSurfaceStyleRendering",
**{
"SurfaceColour": self.create_colour_rgb(self.settings["surface_colour"]),
"Transparency": self.settings["transparency"],
"ReflectanceMethod": "NOTDEFINED",
"DiffuseColour": self.create_colour_rgb(self.settings["diffuse_colour"]),
}
)
def create_externally_defined_surface_style(self):
self.file.create_entity(
"IfcExternallyDefinedSurfaceStyle",
**{
"Location": self.settings["location"],
"Identification": self.settings["identification"],
"Name": self.settings["name"],
}
)
def create_colour_rgb(self, colour):
return self.file.createIfcColourRgb(None, colour[0], colour[1], colour[2])
@@ -0,0 +1,27 @@
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"style": None, "ifc_class": "IfcSurfaceStyleRendering", "attributes": {}}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
for key, value in self.settings["attributes"].items():
if key == "SurfaceColour" and value:
self.settings["attributes"][key] = self.create_colour_rgb(value)
if key == "DiffuseColour" and isinstance(value, dict):
self.settings["attributes"][key] = self.create_colour_rgb(value)
style_item = self.file.create_entity(self.settings["ifc_class"], **self.settings["attributes"])
styles = list(self.settings["style"].Styles or [])
duplicate_items = [s for s in styles if s.is_a(self.settings["ifc_class"])]
for duplicate_item in duplicate_items:
self.file.remove(duplicate_item)
styles.append(style_item)
self.settings["style"].Styles = styles
return style_item
def create_colour_rgb(self, value):
return self.file.createIfcColourRgb(value["Name"], value["Red"], value["Green"], value["Blue"])
@@ -15,9 +15,9 @@ class Usecase:
setattr(self.settings["style"], key, value) setattr(self.settings["style"], key, value)
def edit_surface_colour(self, value): def edit_surface_colour(self, value):
self.settings["style"].SurfaceColour[1] = value[0] self.settings["style"].SurfaceColour[1] = value["Red"]
self.settings["style"].SurfaceColour[2] = value[1] self.settings["style"].SurfaceColour[2] = value["Green"]
self.settings["style"].SurfaceColour[3] = value[2] self.settings["style"].SurfaceColour[3] = value["Blue"]
def is_colour_or_factor(self, name): def is_colour_or_factor(self, name):
return name in [ return name in [
@@ -29,15 +29,15 @@ class Usecase:
] ]
def edit_colour_or_factor(self, name, value): def edit_colour_or_factor(self, name, value):
if isinstance(value, (list, tuple)): if isinstance(value, dict):
attribute = getattr(self.settings["style"], name) attribute = getattr(self.settings["style"], name)
if not attribute or not attribute.is_a("IfcColourRgb"): if not attribute or not attribute.is_a("IfcColourRgb"):
colour = self.file.createIfcColourRgb(None, 0, 0, 0) colour = self.file.createIfcColourRgb(None, 0, 0, 0)
setattr(self.settings["style"], name, colour) setattr(self.settings["style"], name, colour)
attribute = getattr(self.settings["style"], name) attribute = getattr(self.settings["style"], name)
attribute[1] = value[0] attribute[1] = value["Red"]
attribute[2] = value[1] attribute[2] = value["Green"]
attribute[3] = value[2] attribute[3] = value["Blue"]
else: else:
existing_value = getattr(self.settings["style"], name) existing_value = getattr(self.settings["style"], name)
if existing_value and existing_value.id(): if existing_value and existing_value.id():