Potentially risky refactor of add_representation in preparation of stabilisation and optimization of geometric operators.

This commit is contained in:
Dion Moult
2021-10-18 19:23:40 +11:00
parent 33f4461a87
commit 4577b7bf60
16 changed files with 561 additions and 97 deletions
@@ -66,87 +66,30 @@ class AddRepresentation(bpy.types.Operator, Operator):
profile_set_usage: bpy.props.IntProperty()
def _execute(self, context):
ifc_context = self.context_id or int(context.scene.BIMProperties.contexts or "0") or None
if ifc_context:
ifc_context = tool.Ifc.get().by_id(ifc_context)
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file()
core.edit_object_placement(tool.Ifc, tool.Surveyor, obj=obj)
if not obj.data:
return {"FINISHED"}
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
context_id = self.context_id or int(context.scene.BIMProperties.contexts)
context_of_items = self.file.by_id(context_id)
gprop = context.scene.BIMGeoreferenceProperties
coordinate_offset = None
if gprop.has_blender_offset and obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT":
coordinate_offset = Vector(
(
float(gprop.blender_eastings),
float(gprop.blender_northings),
float(gprop.blender_orthogonal_height),
)
)
representation_data = {
"context": context_of_items,
"blender_object": obj,
"geometry": obj.data,
"coordinate_offset": coordinate_offset,
"total_items": max(1, len(obj.material_slots)),
"should_force_faceted_brep": context.scene.BIMGeometryProperties.should_force_faceted_brep,
"should_force_triangulation": context.scene.BIMGeometryProperties.should_force_triangulation,
"ifc_representation_class": self.ifc_representation_class,
"profile_set_usage": self.file.by_id(self.profile_set_usage) if self.profile_set_usage else None,
}
result = ifcopenshell.api.run("geometry.add_representation", self.file, **representation_data)
if not result:
print("Failed to write shape representation")
return {"FINISHED"}
[
blenderbim.core.style.add_style(tool.Ifc, tool.Style, obj=s.material)
for s in obj.material_slots
if s.material and not s.material.BIMMaterialProperties.ifc_style_id
]
if isinstance(obj.data, bpy.types.Mesh) and len(obj.data.polygons):
ifcopenshell.api.run(
"style.assign_representation_styles",
self.file,
**{
"shape_representation": result,
"styles": [
self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id)
for s in obj.material_slots
if s.material
],
"should_use_presentation_style_assignment": context.scene.BIMGeometryProperties.should_use_presentation_style_assignment,
},
)
ifcopenshell.api.run(
"geometry.assign_representation", self.file, **{"product": product, "representation": result}
core.add_representation(
tool.Ifc,
tool.Geometry,
tool.Style,
tool.Surveyor,
obj=obj,
context=ifc_context,
ifc_representation_class=self.ifc_representation_class,
profile_set_usage=tool.Ifc.get().by_id(self.profile_set_usage) if self.profile_set_usage else None,
)
mesh = obj.data.copy()
mesh.name = "{}/{}".format(context_id, result.id())
mesh.BIMMeshProperties.ifc_definition_id = int(result.id())
obj.data = mesh
Data.load(self.file, obj.BIMObjectProperties.ifc_definition_id)
if product.is_a("IfcTypeProduct"):
if self.file.schema == "IFC2X3":
types = product.ObjectTypeOf
Data.load(tool.Ifc.get(), obj.BIMObjectProperties.ifc_definition_id)
element = tool.Ifc.get_entity(obj)
if element.is_a("IfcTypeProduct"):
if tool.Ifc.get_schema() == "IFC2X3":
types = element.ObjectTypeOf
else:
types = product.Types
types = element.Types
if types:
for element in types[0].RelatedObjects:
Data.load(self.file, element.id())
return {"FINISHED"}
Data.load(tool.Ifc.get(), element.id())
class SwitchRepresentation(bpy.types.Operator):
@@ -430,7 +373,16 @@ class CopyRepresentation(bpy.types.Operator, Operator):
continue
if obj.data:
bm.to_mesh(obj.data)
bpy.ops.bim.add_representation(obj=obj.name)
core.add_representation(
tool.Ifc,
tool.Geometry,
tool.Style,
tool.Surveyor,
obj=obj,
context=tool.Ifc.get().by_id(int(context.scene.BIMProperties.contexts)),
ifc_representation_class=None,
profile_set_usage=None,
)
class OverrideDelete(bpy.types.Operator):
@@ -25,6 +25,8 @@ import ifcopenshell.util.unit
import ifcopenshell.util.element
import mathutils.geometry
import blenderbim.bim.handler
import blenderbim.tool as tool
import blenderbim.core.geometry
from blenderbim.bim.ifc import IfcStore
from math import pi, degrees, inf
from mathutils import Vector, Matrix
@@ -169,11 +171,15 @@ class DumbProfileGenerator:
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
bpy.ops.bim.assign_type(relating_type=self.relating_type.id(), related_object=obj.name)
profile_set_usage = ifcopenshell.util.element.get_material(element)
bpy.ops.bim.add_representation(
obj=obj.name,
context_id=ifcopenshell.util.representation.get_context(self.file, "Model", "Body", "MODEL_VIEW").id(),
blenderbim.core.geometry.add_representation(
tool.Ifc,
tool.Geometry,
tool.Style,
tool.Surveyor,
obj=obj,
context=ifcopenshell.util.representation.get_context(self.file, "Model", "Body", "MODEL_VIEW"),
ifc_representation_class="IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage",
profile_set_usage=profile_set_usage.id(),
profile_set_usage=profile_set_usage,
)
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
bpy.ops.bim.switch_representation(
@@ -32,6 +32,7 @@ import blenderbim.core.owner
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim import import_ifc
from blenderbim.bim import export_ifc
from ifcopenshell.api.context.data import Data as ContextData
class CreateProject(bpy.types.Operator):
@@ -76,7 +77,7 @@ class CreateProject(bpy.types.Operator):
model = blenderbim.core.context.add_context(
tool.Ifc, context_type="Model", context_identifier="", target_view="", parent=0
)
blenderbim.core.context.add_context(
body_context = blenderbim.core.context.add_context(
tool.Ifc, context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model
)
blenderbim.core.context.add_context(
@@ -89,9 +90,8 @@ class CreateProject(bpy.types.Operator):
tool.Ifc, context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent=plan
)
context.scene.BIMProperties.contexts = str(
ifcopenshell.util.representation.get_context(self.file, "Model", "Body", "MODEL_VIEW").id()
)
ContextData.load(tool.Ifc.get())
context.scene.BIMProperties.contexts = str(body_context.id())
bpy.ops.bim.assign_class(obj=site.name, ifc_class="IfcSite")
bpy.ops.bim.assign_class(obj=building.name, ifc_class="IfcBuilding")
@@ -24,6 +24,7 @@ import ifcopenshell.util.element
import blenderbim.bim.handler
import blenderbim.core.spatial
import blenderbim.core.style
import blenderbim.core.geometry
import blenderbim.core.material
import blenderbim.tool as tool
from ifcopenshell.api.void.data import Data as VoidData
@@ -144,8 +145,18 @@ class AssignClass(bpy.types.Operator):
IfcStore.link_element(product, obj)
if self.should_add_representation:
bpy.ops.bim.add_representation(
obj=obj.name, context_id=self.context_id, ifc_representation_class=self.ifc_representation_class
ifc_context = self.context_id or int(context.scene.BIMProperties.contexts or "0") or None
if ifc_context:
ifc_context = tool.Ifc.get().by_id(ifc_context)
blenderbim.core.geometry.add_representation(
tool.Ifc,
tool.Geometry,
tool.Style,
tool.Surveyor,
obj=obj,
context=ifc_context,
ifc_representation_class=self.ifc_representation_class,
profile_set_usage=None,
)
if product.is_a("IfcElementType"):
@@ -351,7 +362,16 @@ class CopyClass(bpy.types.Operator):
if relating_type and relating_type.RepresentationMaps:
bpy.ops.bim.assign_type(relating_type=relating_type.id(), related_object=obj.name)
else:
bpy.ops.bim.add_representation(obj=obj.name)
blenderbim.core.geometry.add_representation(
tool.Ifc,
tool.Geometry,
tool.Style,
tool.Surveyor,
obj=obj,
context=tool.Ifc.get().by_id(int(context.scene.BIMProperties.contexts)),
ifc_representation_class=None,
profile_set_usage=None,
)
if result.is_a("IfcSpatialElement") or result.is_a("IfcSpatialStructureElement"):
tool.Collector.assign(obj)
elif result.is_a("IfcOpeningElement"):
@@ -16,8 +16,58 @@
# 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
def edit_object_placement(ifc, surveyor, obj=None):
element = ifc.get_entity(obj)
if element:
ifc.run("geometry.edit_object_placement", product=element, matrix=surveyor.get_absolute_matrix(obj))
def add_representation(
ifc, geometry, style, surveyor, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
):
element = ifc.get_entity(obj)
if not element:
return
edit_object_placement(ifc, surveyor, obj=obj)
data = geometry.get_object_data(obj)
if not data:
return
representation = ifc.run(
"geometry.add_representation",
context=context,
blender_object=obj,
geometry=data,
coordinate_offset=geometry.get_cartesian_point_coordinate_offset(obj),
total_items=geometry.get_total_representation_items(obj),
should_force_faceted_brep=geometry.should_force_faceted_brep(),
should_force_triangulation=geometry.should_force_triangulation(),
ifc_representation_class=ifc_representation_class,
profile_set_usage=profile_set_usage,
)
if geometry.does_object_have_mesh_with_faces(obj):
styles = [
blenderbim.core.style.add_style(ifc, style, obj=material)
for material in geometry.get_object_materials_without_styles(obj)
]
ifc.run(
"style.assign_representation_styles",
shape_representation=representation,
styles=styles,
should_use_presentation_style_assignment=geometry.should_use_presentation_style_assignment(),
)
ifc.run("geometry.assign_representation", product=element, representation=representation)
data = geometry.duplicate_object_data(obj)
name = geometry.get_representation_name(context, representation)
geometry.rename_object_data(data, name)
geometry.link(representation, data)
return representation
+16
View File
@@ -63,6 +63,22 @@ class Context:
def set_context(cls, context): pass
@interface
class Geometry:
def does_object_have_mesh_with_faces(cls, obj): pass
def duplicate_object_data(cls, obj): pass
def get_object_data(cls, obj): pass
def get_object_materials_without_styles(cls, obj): pass
def get_representation_name(cls, context, representation): pass
def get_cartesian_point_coordinate_offset(cls, obj): pass
def get_total_representation_items(cls, obj): pass
def link(cls, element, obj): pass
def rename_object_data(cls, data, name): pass
def should_force_faceted_brep(cls): pass
def should_force_triangulation(cls): pass
def should_use_presentation_style_assignment(cls): pass
@interface
class Ifc:
def get(cls): pass
@@ -21,6 +21,7 @@ from blenderbim.tool.blender import Blender
from blenderbim.tool.collector import Collector
from blenderbim.tool.container import Container
from blenderbim.tool.context import Context
from blenderbim.tool.geometry import Geometry
from blenderbim.tool.ifc import Ifc
from blenderbim.tool.material import Material
from blenderbim.tool.misc import Misc
@@ -0,0 +1,84 @@
# 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.tool as tool
from mathutils import Vector
from blenderbim.bim.ifc import IfcStore
class Geometry(blenderbim.core.tool.Geometry):
@classmethod
def does_object_have_mesh_with_faces(cls, obj):
return bool(isinstance(obj.data, bpy.types.Mesh) and len(obj.data.polygons))
@classmethod
def duplicate_object_data(cls, obj):
obj.data = obj.data.copy()
return obj.data
@classmethod
def get_object_data(cls, obj):
return obj.data
@classmethod
def get_object_materials_without_styles(cls, obj):
return [
s.material for s in obj.material_slots if s.material and not s.material.BIMMaterialProperties.ifc_style_id
]
@classmethod
def get_representation_name(cls, context, representation):
return f"{context.id()}/{representation.id()}"
@classmethod
def get_cartesian_point_coordinate_offset(cls, obj):
props = bpy.context.scene.BIMGeoreferenceProperties
if props.has_blender_offset and obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT":
return Vector(
(
float(props.blender_eastings),
float(props.blender_northings),
float(props.blender_orthogonal_height),
)
)
@classmethod
def get_total_representation_items(cls, obj):
return max(1, len(obj.material_slots))
@classmethod
def link(cls, element, obj):
obj.BIMMeshProperties.ifc_definition_id = element.id()
@classmethod
def rename_object_data(cls, data, name):
data.name = name
@classmethod
def should_force_faceted_brep(cls):
return bpy.context.scene.BIMGeometryProperties.should_force_faceted_brep
@classmethod
def should_force_triangulation(cls):
return bpy.context.scene.BIMGeometryProperties.should_force_triangulation
@classmethod
def should_use_presentation_style_assignment(cls):
return bpy.context.scene.BIMGeometryProperties.should_use_presentation_style_assignment
+3 -1
View File
@@ -83,7 +83,9 @@ class Misc(blenderbim.core.tool.Misc):
min_z = min([c[2] for c in absolute_bound_box])
current_absolute_height = max_z - min_z
scale_factor = height / current_absolute_height
obj.matrix_world @= Matrix.Scale(scale_factor, 4, obj.matrix_world.to_quaternion() @ Vector((0, 0, 1)))
obj.matrix_world @= Matrix.Scale(
scale_factor, 4, obj.matrix_world.inverted().to_quaternion() @ Vector((0, 0, 1))
)
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
@classmethod
@@ -11,6 +11,18 @@ Scenario: Edit object placement
When I press "bim.edit_object_placement"
Then nothing happens
Scenario: Add representation
Given an empty IFC project
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
And the object "IfcWall/Cube" is selected
When the variable "context" is "{ifc}.by_type('IfcGeometricRepresentationSubContext')[-1].id()"
And I set "scene.BIMProperties.contexts" to "{context}"
And I press "bim.add_representation"
Then nothing happens
Scenario: Copy representation
Given an empty IFC project
And I add a cube
+1
View File
@@ -130,6 +130,7 @@ def additionally_the_object_name_is_selected(name):
@given(parsers.parse('I set "{prop}" to "{value}"'))
@when(parsers.parse('I set "{prop}" to "{value}"'))
def i_set_prop_to_value(prop, value):
value = replace_variables(value)
try:
eval(f"bpy.context.{prop}")
except:
+7
View File
@@ -49,6 +49,13 @@ def context():
prophet.verify()
@pytest.fixture
def geometry():
prophet = Prophecy(blenderbim.core.tool.Geometry)
yield prophet
prophet.verify()
@pytest.fixture
def material():
prophet = Prophecy(blenderbim.core.tool.Material)
+152 -2
View File
@@ -17,12 +17,162 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import blenderbim.core.geometry as subject
from test.core.bootstrap import ifc, surveyor
import test.core.test_style
from test.core.bootstrap import ifc, surveyor, geometry, style
class TestEditObjectPlacement:
def test_run(self, ifc, surveyor):
def predict(self, ifc, surveyor):
ifc.get_entity("obj").should_be_called().will_return("element")
surveyor.get_absolute_matrix("obj").should_be_called().will_return("matrix")
ifc.run("geometry.edit_object_placement", product="element", matrix="matrix").should_be_called()
def test_run(self, ifc, surveyor):
self.predict(ifc, surveyor)
subject.edit_object_placement(ifc, surveyor, obj="obj")
class TestAddRepresentation:
def test_run(self, ifc, geometry, style, surveyor):
TestEditObjectPlacement.predict(self, ifc, surveyor)
# Add representation
geometry.get_object_data("obj").should_be_called().will_return("data")
geometry.get_cartesian_point_coordinate_offset("obj").should_be_called().will_return("coordinate_offset")
geometry.get_total_representation_items("obj").should_be_called().will_return(1)
geometry.should_force_faceted_brep().should_be_called().will_return(False)
geometry.should_force_triangulation().should_be_called().will_return(True)
ifc.run(
"geometry.add_representation",
context="context",
blender_object="obj",
geometry="data",
coordinate_offset="coordinate_offset",
total_items=1,
should_force_faceted_brep=False,
should_force_triangulation=True,
ifc_representation_class="ifc_representation_class",
profile_set_usage="profile_set_usage",
).should_be_called().will_return("representation")
# Styles are relevant for meshes with faces only
geometry.does_object_have_mesh_with_faces("obj").should_be_called().will_return(True)
# Add styles
geometry.get_object_materials_without_styles("obj").should_be_called().will_return(["material"])
test.core.test_style.TestAddStyle.predict(self, ifc, style, obj="material")
# Link style to representation items
geometry.should_use_presentation_style_assignment().should_be_called().will_return(False)
ifc.run(
"style.assign_representation_styles",
shape_representation="representation",
styles=["style"],
should_use_presentation_style_assignment=False,
).should_be_called()
# Assign representation to product
ifc.run("geometry.assign_representation", product="element", representation="representation").should_be_called()
# Update mesh
geometry.duplicate_object_data("obj").should_be_called().will_return("data")
geometry.get_representation_name("context", "representation").should_be_called().will_return("name")
geometry.rename_object_data("data", "name").should_be_called()
geometry.link("representation", "data").should_be_called()
assert (
subject.add_representation(
ifc,
geometry,
style,
surveyor,
obj="obj",
context="context",
ifc_representation_class="ifc_representation_class",
profile_set_usage="profile_set_usage",
)
== "representation"
)
def test_not_handling_styles_if_representation_has_no_faces(self, ifc, geometry, style, surveyor):
TestEditObjectPlacement.predict(self, ifc, surveyor)
# Add representation
geometry.get_object_data("obj").should_be_called().will_return("data")
geometry.get_cartesian_point_coordinate_offset("obj").should_be_called().will_return("coordinate_offset")
geometry.get_total_representation_items("obj").should_be_called().will_return(1)
geometry.should_force_faceted_brep().should_be_called().will_return(False)
geometry.should_force_triangulation().should_be_called().will_return(True)
ifc.run(
"geometry.add_representation",
context="context",
blender_object="obj",
geometry="data",
coordinate_offset="coordinate_offset",
total_items=1,
should_force_faceted_brep=False,
should_force_triangulation=True,
ifc_representation_class="ifc_representation_class",
profile_set_usage="profile_set_usage",
).should_be_called().will_return("representation")
# Styles are relevant for meshes with faces only
geometry.does_object_have_mesh_with_faces("obj").should_be_called().will_return(False)
# Assign representation to product
ifc.run("geometry.assign_representation", product="element", representation="representation").should_be_called()
# Update mesh
geometry.duplicate_object_data("obj").should_be_called().will_return("data")
geometry.get_representation_name("context", "representation").should_be_called().will_return("name")
geometry.rename_object_data("data", "name").should_be_called()
geometry.link("representation", "data").should_be_called()
assert (
subject.add_representation(
ifc,
geometry,
style,
surveyor,
obj="obj",
context="context",
ifc_representation_class="ifc_representation_class",
profile_set_usage="profile_set_usage",
)
== "representation"
)
def test_only_updating_the_placement_if_there_is_no_object_data(self, ifc, geometry, style, surveyor):
TestEditObjectPlacement.predict(self, ifc, surveyor)
# Add representation
geometry.get_object_data("obj").should_be_called().will_return(None)
assert (
subject.add_representation(
ifc,
geometry,
style,
surveyor,
obj="obj",
context="context",
ifc_representation_class="ifc_representation_class",
profile_set_usage="profile_set_usage",
)
is None
)
def test_doing_nothing_if_not_an_ifc_element(self, ifc, geometry, style, surveyor):
ifc.get_entity("obj").should_be_called().will_return(None)
assert (
subject.add_representation(
ifc,
geometry,
style,
surveyor,
obj="obj",
context="context",
ifc_representation_class="ifc_representation_class",
profile_set_usage="profile_set_usage",
)
is None
)
+8 -5
View File
@@ -21,15 +21,18 @@ 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")
def predict(self, ifc, style, obj="obj"):
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")
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)
ifc.get_entity(obj).should_be_called().will_return(None)
def test_it_adds_a_style_with_rendering_attributes(self, ifc, style):
self.predict(ifc, style)
assert subject.add_style(ifc, style, obj="obj") == "style"
def test_adding_a_style_linked_to_a_material(self, ifc, style):
+161
View File
@@ -0,0 +1,161 @@
# 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 math
import numpy
import ifcopenshell
import test.bim.bootstrap
import blenderbim.core.tool
import blenderbim.tool as tool
from mathutils import Vector
from blenderbim.tool.geometry import Geometry as subject
from blenderbim.bim.ifc import IfcStore
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Geometry)
class TestDoesObjectHaveMeshWithFaces(test.bim.bootstrap.NewFile):
def test_empties_return_false(self):
obj = bpy.data.objects.new("Object", None)
assert subject.does_object_have_mesh_with_faces(obj) is False
def test_non_meshes_return_false(self):
obj = bpy.data.objects.new("Object", bpy.data.cameras.new("Curve"))
assert subject.does_object_have_mesh_with_faces(obj) is False
def test_meshes_without_faces_return_false(self):
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
assert subject.does_object_have_mesh_with_faces(obj) is False
def test_meshes_with_faces_return_true(self):
bpy.ops.mesh.primitive_cube_add()
obj = bpy.data.objects.get("Cube")
assert subject.does_object_have_mesh_with_faces(obj) is True
class TestDuplicateObjectData(test.bim.bootstrap.NewFile):
def test_run(self):
data = bpy.data.meshes.new("Mesh")
obj = bpy.data.objects.new("Object", data)
assert subject.duplicate_object_data(obj) == obj.data
assert obj.data != data
assert isinstance(obj.data, bpy.types.Mesh)
class TestGetObjectData(test.bim.bootstrap.NewFile):
def test_run(self):
data = bpy.data.meshes.new("Mesh")
obj = bpy.data.objects.new("Object", data)
assert subject.get_object_data(obj) == obj.data
class TestGetObjectMaterialsWithoutStyles(test.bim.bootstrap.NewFile):
def test_run(self):
material1 = bpy.data.materials.new("Material")
material2 = bpy.data.materials.new("Material")
material3 = bpy.data.materials.new("Material")
material3.BIMMaterialProperties.ifc_style_id = 1
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
obj.data.materials.append(material1)
obj.data.materials.append(material2)
obj.data.materials.append(material3)
assert subject.get_object_materials_without_styles(obj) == [material1, material2]
class TestGetRepresentationName(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
context = ifc.createIfcGeometricRepresentationContext()
representation = ifc.createIfcShapeRepresentation()
assert subject.get_representation_name(context, representation) == f"{context.id()}/{representation.id()}"
class TestGetCartesianPointCoordinateOffset(test.bim.bootstrap.NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT"
props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = True
props.blender_eastings = "1"
props.blender_northings = "2"
props.blender_orthogonal_height = "3"
assert subject.get_cartesian_point_coordinate_offset(obj) == Vector((1.0, 2.0, 3.0))
def test_get_null_if_not_a_cartesian_point_offset_type(self):
obj = bpy.data.objects.new("Object", None)
props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = True
props.blender_eastings = "1"
props.blender_northings = "2"
props.blender_orthogonal_height = "3"
assert subject.get_cartesian_point_coordinate_offset(obj) is None
def test_get_null_if_no_blender_offset(self):
obj = bpy.data.objects.new("Object", None)
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT"
props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = False
assert subject.get_cartesian_point_coordinate_offset(obj) is None
class TestGetTotalRepresentationItems(test.bim.bootstrap.NewFile):
def test_run(self):
material1 = bpy.data.materials.new("Material")
material2 = bpy.data.materials.new("Material")
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
obj.data.materials.append(material1)
obj.data.materials.append(material2)
assert subject.get_total_representation_items(obj) == 2
class TestLink(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
element = ifc.createIfcShapeRepresentation()
obj = bpy.data.meshes.new("Mesh")
subject.link(element, obj)
assert obj.BIMMeshProperties.ifc_definition_id == element.id()
class TestRenameObjectData(test.bim.bootstrap.NewFile):
def test_run(self):
obj = bpy.data.meshes.new("Mesh")
subject.rename_object_data(obj, "name")
assert obj.name == "name"
class TestShouldForceFacetedBrep(test.bim.bootstrap.NewFile):
def test_run(self):
result = bpy.context.scene.BIMGeometryProperties.should_force_faceted_brep
assert subject.should_force_faceted_brep() is result
class TestShouldForceTriangulation(test.bim.bootstrap.NewFile):
def test_run(self):
result = bpy.context.scene.BIMGeometryProperties.should_force_triangulation
assert subject.should_force_triangulation() is result
class TestShouldUsePresentationStyleAssignment(test.bim.bootstrap.NewFile):
def test_run(self):
result = bpy.context.scene.BIMGeometryProperties.should_use_presentation_style_assignment
assert subject.should_use_presentation_style_assignment() is result
@@ -10,7 +10,6 @@ class Usecase:
self.settings[key] = value
def execute(self):
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self.file.schema)
result = ifcopenshell.util.element.copy(self.file, self.settings["product"])
self.copy_direct_attributes(result)
self.copy_indirect_attributes(self.settings["product"], result)