Rename source dir

This commit is contained in:
Dion Moult
2024-08-13 23:09:50 +10:00
parent cc28f5a92b
commit 25071dfec6
930 changed files with 0 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
+142
View File
@@ -0,0 +1,142 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.api.geometry
import ifcopenshell.api.root
import ifcopenshell.api.unit
import ifcopenshell.api.context
import ifcopenshell.util.shape_builder
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.aggregate import Aggregate as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Aggregate)
class TestCanAggregate(NewFile):
def test_elements_can_aggregate(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcElementAssembly()
element_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, element_obj)
subelement = ifc.createIfcBeam()
subelement_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(subelement, subelement_obj)
assert subject.can_aggregate(element_obj, subelement_obj) is True
def test_spatial_elements_can_aggregate(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcSite()
element_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, element_obj)
subelement = ifc.createIfcBuilding()
subelement_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(subelement, subelement_obj)
assert subject.can_aggregate(element_obj, subelement_obj) is True
def test_spatial_elements_can_aggregate_2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
element = ifc.createIfcSite()
element_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, element_obj)
subelement = ifc.createIfcBuilding()
subelement_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(subelement, subelement_obj)
assert subject.can_aggregate(element_obj, subelement_obj) is True
def test_spatial_elements_and_projects_can_aggregate(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcProject()
element_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, element_obj)
subelement = ifc.createIfcBuilding()
subelement_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(subelement, subelement_obj)
assert subject.can_aggregate(element_obj, subelement_obj) is True
def test_spatial_elements_and_projects_can_aggregate_2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
element = ifc.createIfcProject()
element_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, element_obj)
subelement = ifc.createIfcBuilding()
subelement_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(subelement, subelement_obj)
assert subject.can_aggregate(element_obj, subelement_obj) is True
def test_unlinked_elements_cannot_aggregate(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element_obj = bpy.data.objects.new("Object", None)
subelement_obj = bpy.data.objects.new("Object", None)
assert subject.can_aggregate(element_obj, subelement_obj) is False
class TestHasPhysicalBodyRepresentation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
element = ifc.createIfcElementAssembly()
assert subject.has_physical_body_representation(element) is False
ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcProject")
ifcopenshell.api.unit.assign_unit(ifc)
context = ifcopenshell.api.context.add_context(ifc, context_type="Model")
body = ifcopenshell.api.context.add_context(
ifc, context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=context
)
builder = ifcopenshell.util.shape_builder.ShapeBuilder(ifc)
origin = builder.create_axis2_placement_3d()
block = ifc.createIfcCsgSolid(ifc.createIfcBlock(origin, 200, 200, 200))
rep = builder.get_representation(context=body, items=[block])
ifcopenshell.api.geometry.assign_representation(ifc, product=element, representation=rep)
assert subject.has_physical_body_representation(element) is True
class TestDisableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
subject.disable_editing(obj)
assert obj.BIMObjectAggregateProperties.is_editing is False
class TestEnableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
assert obj.BIMObjectAggregateProperties.is_editing is True
class TestGetContainer(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcWall()
container = ifc.createIfcBuildingStorey()
ifcopenshell.api.run("spatial.assign_container", ifc, products=[element], relating_structure=container)
assert subject.get_container(element) == container
+61
View File
@@ -0,0 +1,61 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
import pytest
from test.bim.bootstrap import NewFile
from blenderbim.tool.blender import Blender as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Blender)
class TestCopyNodeGraph(NewFile):
def test_run(self):
material_to = bpy.data.materials.new("material_to")
material_to.use_nodes = True
material_to_nodes = material_to.node_tree.nodes
assert len(material_to_nodes) == 2
for node in material_to_nodes:
material_to_nodes.remove(node)
assert len(material_to_nodes) == 0
material_from = bpy.data.materials.new("material_from")
material_from.use_nodes = True
subject.copy_node_graph(material_to, material_from)
assert len(material_to_nodes) == 2
class TestSortPanelsForRegister(NewFile):
def test_run(self):
items = ["A", "B", "C", "D"]
items_to_parents = {"A": "D", "D": "C", "C": "B"}
sorted_items = subject.sort_panels_for_register(items, items_to_parents)
assert tuple(sorted_items) == ("B", "C", "D", "A")
with pytest.raises(AssertionError):
subject.sort_panels_for_register(items, {"A": "K"})
with pytest.raises(AssertionError):
subject.sort_panels_for_register(items, {"J": "A"})
+540
View File
@@ -0,0 +1,540 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import os
import bpy
import brickschema
import brickschema.persistent
from brickschema.namespaces import REF, A
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.guid
import blenderbim.core.tool
import blenderbim.tool as tool
from rdflib.namespace import RDF
from rdflib import Literal, URIRef, Namespace
from test.bim.bootstrap import NewFile
from blenderbim.tool.brick import Brick as subject
from blenderbim.tool.brick import BrickStore
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Brick)
class TestAddBrick(NewFile):
def test_run(self):
BrickStore.graph = brickschema.persistent.VersionedGraphCollection("sqlite://")
result = subject.add_brick(
"https://example.org/digitaltwin#", "https://brickschema.org/schema/Brick#Equipment", "label"
)
assert "https://example.org/digitaltwin#" in result
assert list(
BrickStore.graph.triples((URIRef(result), A, URIRef("https://brickschema.org/schema/Brick#Equipment")))
)
assert list(
BrickStore.graph.triples(
(URIRef(result), URIRef("http://www.w3.org/2000/01/rdf-schema#label"), Literal("label"))
)
)
class TestAddBrickBreadcrumb(NewFile):
def test_run(self):
subject.set_active_brick_class("brick_class")
subject.add_brick_breadcrumb()
assert bpy.context.scene.BIMBrickProperties.brick_breadcrumbs[0].name == "brick_class"
subject.add_brick_breadcrumb()
assert bpy.context.scene.BIMBrickProperties.brick_breadcrumbs[1].name == "brick_class"
def test_run_split_screen(self):
subject.set_active_brick_class("brick_class", split_screen=True)
subject.add_brick_breadcrumb(split_screen=True)
assert bpy.context.scene.BIMBrickProperties.split_screen_brick_breadcrumbs[0].name == "brick_class"
subject.add_brick_breadcrumb(split_screen=True)
assert bpy.context.scene.BIMBrickProperties.split_screen_brick_breadcrumbs[1].name == "brick_class"
class TestAddBrickFromElement(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
element = ifc.createIfcChiller()
element.Name = "Chiller"
element.GlobalId = ifcopenshell.guid.new()
BrickStore.graph = brickschema.persistent.VersionedGraphCollection("sqlite://")
result = subject.add_brick_from_element(
element, "http://example.org/digitaltwin#", "https://brickschema.org/schema/Brick#Equipment"
)
uri = f"http://example.org/digitaltwin#{element.GlobalId}"
assert result == uri
assert list(
BrickStore.graph.triples((URIRef(uri), A, URIRef("https://brickschema.org/schema/Brick#Equipment")))
)
assert list(
BrickStore.graph.triples(
(URIRef(uri), URIRef("http://www.w3.org/2000/01/rdf-schema#label"), Literal("Chiller"))
)
)
def test_run_no_element_name(self):
ifc = ifcopenshell.file()
element = ifc.createIfcChiller()
element.GlobalId = ifcopenshell.guid.new()
BrickStore.graph = brickschema.persistent.VersionedGraphCollection("sqlite://")
result = subject.add_brick_from_element(
element, "http://example.org/digitaltwin#", "https://brickschema.org/schema/Brick#Equipment"
)
uri = f"http://example.org/digitaltwin#{element.GlobalId}"
assert result == uri
assert list(
BrickStore.graph.triples((URIRef(uri), A, URIRef("https://brickschema.org/schema/Brick#Equipment")))
)
assert list(
BrickStore.graph.triples(
(URIRef(uri), URIRef("http://www.w3.org/2000/01/rdf-schema#label"), Literal("Unnamed"))
)
)
class TestAddBrickifcProject(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
project = ifc.createIfcProject(ifcopenshell.guid.new())
project.Name = "My Project"
BrickStore.graph = brickschema.persistent.VersionedGraphCollection("sqlite://")
result = subject.add_brickifc_project("http://example.org/digitaltwin#")
assert result == f"http://example.org/digitaltwin#{project.GlobalId}"
brick = URIRef(result)
assert list(BrickStore.graph.triples((brick, A, REF.ifcProject)))
assert list(BrickStore.graph.triples((brick, REF.ifcProjectID, Literal(project.GlobalId))))
assert list(
BrickStore.graph.triples((brick, REF.ifcFileLocation, Literal(bpy.context.scene.BIMProperties.ifc_file)))
)
assert list(
BrickStore.graph.triples(
(brick, URIRef("http://www.w3.org/2000/01/rdf-schema#label"), Literal("My Project"))
)
)
class TestAddBrickifcReference(NewFile):
def test_run(self):
TestAddBrickifcProject().test_run()
element = tool.Ifc.get().createIfcChiller(ifcopenshell.guid.new())
element.Name = "Chiller"
project = URIRef(f"http://example.org/digitaltwin#{tool.Ifc.get().by_type('IfcProject')[0].GlobalId}")
subject.add_brickifc_reference("http://example.org/digitaltwin#foo", element, project)
brick = URIRef("http://example.org/digitaltwin#foo")
bnode = list(BrickStore.graph.triples((brick, A, REF.IFCReference)))
assert list(BrickStore.graph.triples((bnode, REF.hasIfcProjectReference, URIRef(project))))
assert list(BrickStore.graph.triples((bnode, REF.ifcGlobalID, Literal(element.GlobalId))))
assert list(BrickStore.graph.triples((bnode, REF.ifcName, Literal(element.Name))))
class TestAddRelation(NewFile):
def test_run(self):
BrickStore.graph = brickschema.persistent.VersionedGraphCollection("sqlite://")
source = subject.add_brick(
"http://example.org/digitaltwin#", "https://brickschema.org/schema/Brick#Equipment", "source"
)
destination = subject.add_brick(
"http://example.org/digitaltwin#", "https://brickschema.org/schema/Brick#Equipment", "destination"
)
subject.add_relation(source, "https://brickschema.org/schema/Brick#feeds", destination)
assert list(
BrickStore.graph.triples(
(
URIRef(source),
URIRef("https://brickschema.org/schema/Brick#feeds"),
URIRef(destination),
)
)
)
class TestRemoveRelation(NewFile):
def test_run(self):
TestAddRelation().test_run()
source, relation, destination = list(
BrickStore.graph.triples((None, URIRef("https://brickschema.org/schema/Brick#feeds"), None))
)[0]
subject.remove_relation(source, relation, destination)
assert not list(
BrickStore.graph.triples(
(
URIRef(source),
URIRef(relation),
URIRef(destination),
)
)
)
class TestClearBrickBrowser(NewFile):
def test_run(self):
bpy.context.scene.BIMBrickProperties.bricks.add()
subject.clear_brick_browser()
assert len(bpy.context.scene.BIMBrickProperties.bricks) == 0
def test_run_split_screen(self):
bpy.context.scene.BIMBrickProperties.split_screen_bricks.add()
subject.clear_brick_browser(split_screen=True)
assert len(bpy.context.scene.BIMBrickProperties.split_screen_bricks) == 0
class TestClearProject(NewFile):
def test_run(self):
BrickStore.graph = "graph"
bpy.context.scene.BIMBrickProperties.active_brick_class == "brick_class"
bpy.context.scene.BIMBrickProperties.split_screen_active_brick_class == "brick_class2"
subject.clear_project()
assert BrickStore.graph is None
assert bpy.context.scene.BIMBrickProperties.active_brick_class == ""
assert bpy.context.scene.BIMBrickProperties.split_screen_active_brick_class == ""
class TestExportBrickAttributes(NewFile):
def test_run(self):
BrickStore.graph = brickschema.persistent.VersionedGraphCollection("sqlite://")
brick = subject.add_brick(
"https://example.org/digitaltwin#", "https://brickschema.org/schema/Brick#Equipment", "name"
)
assert subject.export_brick_attributes(brick) == {
"Identification": brick,
"Name": "name",
}
def test_run_ifc2x3(self):
BrickStore.graph = brickschema.persistent.VersionedGraphCollection("sqlite://")
brick = subject.add_brick(
"https://example.org/digitaltwin#", "https://brickschema.org/schema/Brick#Equipment", "name"
)
tool.Ifc.set(ifcopenshell.file(schema="IFC2X3"))
assert subject.export_brick_attributes(brick) == {
"ItemReference": brick,
"Name": "name",
}
class TestGetActiveBrickClass(NewFile):
def test_run(self):
subject.set_active_brick_class("brick_class")
assert subject.get_active_brick_class() == "brick_class"
def test_run_split_screen(self):
subject.set_active_brick_class("brick_class", split_screen=True)
assert subject.get_active_brick_class(split_screen=True) == "brick_class"
class TestGetBrick(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
element = ifc.createIfcChiller()
library = ifc.createIfcLibraryReference(Identification="http://example.org/digitaltwin#globalid")
ifc.createIfcRelAssociatesLibrary(RelatedObjects=[element], RelatingLibrary=library)
assert subject.get_brick(element) == "http://example.org/digitaltwin#globalid"
def test_run_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
element = ifc.createIfcEnergyConversionDevice()
library = ifc.createIfcLibraryReference(ItemReference="http://example.org/digitaltwin#globalid")
ifc.createIfcRelAssociatesLibrary(RelatedObjects=[element], RelatingLibrary=library)
assert subject.get_brick(element) == "http://example.org/digitaltwin#globalid"
class TestGetBrickClass(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
element = ifc.createIfcAirTerminalBox()
assert subject.get_brick_class(element) == "https://brickschema.org/schema/Brick#TerminalUnit"
class TestGetBrickPath(NewFile):
def test_run(self):
TestLoadBrickFile().test_run()
cwd = os.path.dirname(os.path.realpath(__file__))
assert subject.get_brick_path() == os.path.join(cwd, "..", "files", "spaces.ttl")
class TestGetBrickPathName(NewFile):
def test_run(self):
TestLoadBrickFile().test_run()
assert subject.get_brick_path_name() == "spaces.ttl"
class TestGetBrickifcProject(NewFile):
def test_run(self):
TestAddBrickifcProject().test_run()
assert (
subject.get_brickifc_project()
== f"http://example.org/digitaltwin#{tool.Ifc.get().by_type('IfcProject')[0].GlobalId}"
)
class TestGetConvertableBrickElements(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcAirTerminalBox()
ifc.createIfcWall()
assert subject.get_convertable_brick_elements() == {element}
class TestGetConvertableBrickSpaces(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcBuildingStorey()
ifc.createIfcWall()
assert subject.get_convertable_brick_spaces() == {element}
class TestGetConvertableBrickSystems(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcSystem()
ifc.createIfcWall()
assert subject.get_convertable_brick_systems() == {element}
class TestGetParentSpace(NewFile):
def test_run(cls):
ifc = ifcopenshell.file()
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcBuildingStorey")
subelement = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcSpace")
project = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
ifcopenshell.api.run("aggregate.assign_object", ifc, products=[subelement], relating_object=element)
ifcopenshell.api.run("aggregate.assign_object", ifc, products=[element], relating_object=project)
assert subject.get_parent_space(subelement) == element
assert subject.get_parent_space(element) is None
class TestGetElementContainer(NewFile):
def test_nothing(cls):
pass
class TestGetElementSystems(NewFile):
def test_nothing(cls):
pass
class TestGetElementFeeds(NewFile):
def test_run(cls):
pass
class TestGetItemClass(NewFile):
def test_run(self):
TestLoadBrickFile().test_run()
assert subject.get_item_class("https://example.org/digitaltwin#floor") == "Floor"
class TestGetLibraryBrickReference(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
library = ifc.createIfcLibraryInformation()
reference = ifc.createIfcLibraryReference(
Identification="http://example.org/digitaltwin#floor", ReferencedLibrary=library
)
assert subject.get_library_brick_reference(library, "http://example.org/digitaltwin#floor") == reference
def test_run_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
reference = ifc.createIfcLibraryReference(ItemReference="http://example.org/digitaltwin#floor")
library = ifc.createIfcLibraryInformation(LibraryReference=[reference])
assert subject.get_library_brick_reference(library, "http://example.org/digitaltwin#floor") == reference
class TestGetNamespace(NewFile):
def test_run(self):
assert subject.get_namespace("http://example.org/digitaltwin#globalid") == "http://example.org/digitaltwin#"
class TestImportBrickClasses(NewFile):
def test_run(self):
TestLoadBrickFile().test_run()
subject.import_brick_classes("Class")
assert len(bpy.context.scene.BIMBrickProperties.bricks) == 2
brick = bpy.context.scene.BIMBrickProperties.bricks[0]
assert brick.name == "Building"
assert brick.uri == "https://brickschema.org/schema/Brick#Building"
assert brick.total_items == 1
assert not brick.label
brick = bpy.context.scene.BIMBrickProperties.bricks[1]
assert brick.name == "Location"
assert brick.uri == "https://brickschema.org/schema/Brick#Location"
assert brick.total_items == 1
assert not brick.label
def test_run_split_sccreen(self):
TestLoadBrickFile().test_run()
subject.import_brick_classes("Class", split_screen=True)
assert len(bpy.context.scene.BIMBrickProperties.split_screen_bricks) == 2
brick = bpy.context.scene.BIMBrickProperties.split_screen_bricks[0]
assert brick.name == "Building"
assert brick.uri == "https://brickschema.org/schema/Brick#Building"
assert brick.total_items == 1
assert not brick.label
brick = bpy.context.scene.BIMBrickProperties.split_screen_bricks[1]
assert brick.name == "Location"
assert brick.uri == "https://brickschema.org/schema/Brick#Location"
assert brick.total_items == 1
assert not brick.label
class TestImportBrickItems(NewFile):
def test_run(self):
TestLoadBrickFile().test_run()
subject.import_brick_items("Building")
assert len(bpy.context.scene.BIMBrickProperties.bricks) == 1
brick = bpy.context.scene.BIMBrickProperties.bricks[0]
assert brick.name == "bldg"
assert brick.label == "My Building"
assert brick.uri == "https://example.org/digitaltwin#bldg"
assert brick.total_items == 0
def test_run_split_screen(self):
TestLoadBrickFile().test_run()
subject.import_brick_items("Building", split_screen=True)
assert len(bpy.context.scene.BIMBrickProperties.split_screen_bricks) == 1
brick = bpy.context.scene.BIMBrickProperties.split_screen_bricks[0]
assert brick.name == "bldg"
assert brick.label == "My Building"
assert brick.uri == "https://example.org/digitaltwin#bldg"
assert brick.total_items == 0
class TestLoadBrickFile(NewFile):
def test_run(self):
# We stub the schema to make tests run faster
cwd = os.path.dirname(os.path.realpath(__file__))
BrickStore.schema = os.path.join(cwd, "..", "files", "BrickStub.ttl")
# This is the actual test
cwd = os.path.dirname(os.path.realpath(__file__))
filepath = os.path.join(cwd, "..", "files", "spaces.ttl")
subject.load_brick_file(filepath)
assert BrickStore.graph
namespaces = [(ns[0], ns[1].toPython()) for ns in BrickStore.graph.namespaces()]
assert ("brick", "https://brickschema.org/schema/Brick#") in namespaces
class TestNewBrickFile(NewFile):
def test_run(self):
# We stub the schema to make tests run faster
cwd = os.path.dirname(os.path.realpath(__file__))
BrickStore.schema = os.path.join(cwd, "..", "files", "BrickStub.ttl")
# This is the actual test
subject.new_brick_file()
assert BrickStore.graph
namespaces = [(ns[0], ns[1].toPython()) for ns in BrickStore.graph.namespaces()]
assert ("digitaltwin", "https://example.org/digitaltwin#") in namespaces
assert ("brick", "https://brickschema.org/schema/Brick#") in namespaces
assert ("rdfs", "http://www.w3.org/2000/01/rdf-schema#") in namespaces
class TestPopBrickBreadcrumb(NewFile):
def test_run(self):
bpy.context.scene.BIMBrickProperties.brick_breadcrumbs.add().name = "foo"
bpy.context.scene.BIMBrickProperties.brick_breadcrumbs.add().name = "bar"
assert subject.pop_brick_breadcrumb() == "bar"
assert len(bpy.context.scene.BIMBrickProperties.brick_breadcrumbs) == 1
assert bpy.context.scene.BIMBrickProperties.brick_breadcrumbs[0].name == "foo"
def test_run_split_screen(self):
bpy.context.scene.BIMBrickProperties.split_screen_brick_breadcrumbs.add().name = "foo"
bpy.context.scene.BIMBrickProperties.split_screen_brick_breadcrumbs.add().name = "bar"
assert subject.pop_brick_breadcrumb(split_screen=True) == "bar"
assert len(bpy.context.scene.BIMBrickProperties.split_screen_brick_breadcrumbs) == 1
assert bpy.context.scene.BIMBrickProperties.split_screen_brick_breadcrumbs[0].name == "foo"
class TestRemoveBrick(NewFile):
def test_run(self):
BrickStore.graph = brickschema.persistent.VersionedGraphCollection("sqlite://")
result = subject.add_brick(
"http://example.org/digitaltwin#", "https://brickschema.org/schema/Brick#Equipment", "label"
)
subject.remove_brick(result)
assert not list(BrickStore.graph.triples((URIRef(result), None, None)))
def test_run_with_bnode(self):
BrickStore.graph = brickschema.persistent.VersionedGraphCollection("sqlite://")
result = subject.add_brick(
"http://example.org/digitaltwin#", "https://brickschema.org/schema/Brick#Equipment", "label"
)
TestAddBrickifcProject().test_run()
element = tool.Ifc.get().createIfcChiller(ifcopenshell.guid.new())
element.Name = "Chiller"
project = URIRef(f"http://example.org/digitaltwin#{tool.Ifc.get().by_type('IfcProject')[0].GlobalId}")
subject.add_brickifc_reference(result, element, project)
subject.remove_brick(result)
assert not list(BrickStore.graph.triples((URIRef(result), None, None)))
assert not list(BrickStore.graph.triples((None, REF.hasIfcProjectReference, None)))
class TestRunAssignBrickReference(NewFile):
def test_nothing(self):
pass
class TestRunRefreshBrickViewer(NewFile):
def test_nothing(self):
pass
class TestRunViewBrickClass(NewFile):
def test_nothing(self):
pass
class TestSelectBrowserItem(NewFile):
def test_run(self):
subject.set_active_brick_class("brick_class")
assert bpy.context.scene.BIMBrickProperties.active_brick_class == "brick_class"
def test_run(self):
subject.set_active_brick_class("brick_class", split_screen=True)
assert bpy.context.scene.BIMBrickProperties.split_screen_active_brick_class == "brick_class"
class TestSetActiveBrickClass(NewFile):
def test_run(self):
bpy.context.scene.BIMBrickProperties.bricks.add().name = "foo"
bpy.context.scene.BIMBrickProperties.bricks.add().name = "bar"
subject.select_browser_item("namespace#bar")
assert bpy.context.scene.BIMBrickProperties.active_brick_index == 1
class TestSerializeBrick(NewFile):
def test_nothing(self):
pass
class TestAddNamespace(NewFile):
def test_run(self):
BrickStore.graph = brickschema.persistent.VersionedGraphCollection("sqlite://")
subject.add_namespace("digitaltwin", "http://example.org/digitaltwin")
assert ("digitaltwin", "http://example.org/digitaltwin") in BrickStore.namespaces
+89
View File
@@ -0,0 +1,89 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>, @Andrej730
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
from test.bim.bootstrap import NewFile
from blenderbim.tool.cad import Cad as subject
from mathutils import Vector
V = lambda *x: Vector([float(i) for i in x])
class TestAreEdgesCollinear(NewFile):
def test_run(self):
# fmt: off
# Parallel edges but not collinear (different z-coordinates)
assert not subject.are_edges_collinear(
(V(-1,0,-1), V(1,0,-1)),
(V(-1,0,1), V(1,0,1))
)
# One edge is just a point and the other is a line segment.
assert not subject.are_edges_collinear(
(V(1,-1,0), V(1,-1,0)),
(V(-1,1,0), V(1,1,0))
)
# Both edges are collinear and overlap.
assert subject.are_edges_collinear(
(V(0,0,0), V(2,2,2)),
(V(1,1,1), V(3,3,3))
)
# Both edges are collinear but don't overlap.
assert subject.are_edges_collinear(
(V(0,0,0), V(1,1,1)),
(V(2,2,2), V(3,3,3))
)
# Edges are not parallel and not collinear.
assert not subject.are_edges_collinear(
(V(0,0,0), V(1,1,1)),
(V(0,1,0), V(1,0,1))
)
# fmt: on
class TestClosestPoints(NewFile):
def test_run(self):
# non collinear
edge1 = (V(0, 0, 0), V(1, 0, 0))
edge2 = (V(2, 0, 1), V(2, 0, 2))
assert subject.closest_points(edge1, edge2)[0] == (edge1[1], edge2[0])
# check other points
assert subject.closest_points(edge1, edge2)[1] == (edge1[0], edge2[1])
# collinear
edge1 = (V(0, 0, 0), V(1, 0, 0))
edge2 = (V(3, 0, 0), V(2, 0, 0))
assert subject.closest_points(edge1, edge2)[0] == (edge1[1], edge2[1])
# parallel
edge1 = (V(0, 0, 0), V(1, 0, 0))
edge2 = (V(-5, 0, 0), V(-1, 0, 0))
assert subject.closest_points(edge1, edge2)[0] == (edge1[0], edge2[1])
# overlapping
edge1 = (V(0, 0, 0), V(3, 0, 0))
edge2 = (V(2, 0, 0), V(5, 0, 0))
assert subject.closest_points(edge1, edge2)[0] == (edge1[1], edge2[0])
# edge as a point
edge1 = (V(0, 0, 0), V(0, 0, 0))
edge2 = (V(1, 0, 1), V(2, 0, 2))
assert subject.closest_points(edge1, edge2)[0] == (edge1[0], edge2[0])
+287
View File
@@ -0,0 +1,287 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
import blenderbim.core.tool
import blenderbim.tool as tool
from blenderbim.tool.collector import Collector as subject
from test.bim.bootstrap import NewFile, NewIfc, NewIfc4X3
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Collector)
class TestAssign(NewIfc):
def test_walls_are_placed_in_its_spatial_collection(self):
wall_obj = bpy.data.objects.new("Object", None)
wall_element = tool.Ifc.get().createIfcWall()
tool.Ifc.link(wall_element, wall_obj)
bpy.context.scene.collection.objects.link(wall_obj)
ifcopenshell.api.run(
"spatial.assign_container",
tool.Ifc.get(),
products=[wall_element],
relating_structure=tool.Ifc.get().by_type("IfcSite")[0],
)
subject.assign(wall_obj)
assert len(wall_obj.users_collection) == 2
assert "IfcSite" in wall_obj.users_collection[0].name
def test_walls_are_unsorted_if_not_decomposes(self):
wall_obj = bpy.data.objects.new("Object", None)
wall_element = tool.Ifc.get().createIfcWall()
tool.Ifc.link(wall_element, wall_obj)
bpy.context.scene.collection.objects.link(wall_obj)
subject.assign(wall_obj)
assert len(wall_obj.users_collection) == 2
assert "Unsorted" in wall_obj.users_collection[0].name
def test_spatial_structure_elements_are_placed_in_a_collection_of_the_same_name(self):
space_obj = bpy.data.objects.new("IfcSpace/Name", None)
space_element = tool.Ifc.get().createIfcSpace()
tool.Ifc.link(space_element, space_obj)
bpy.context.scene.collection.objects.link(space_obj)
ifcopenshell.api.run(
"aggregate.assign_object",
tool.Ifc.get(),
relating_object=tool.Ifc.get().by_type("IfcSite")[0],
products=[space_element],
)
subject.assign(space_obj)
assert len(space_obj.users_collection) == 2
assert space_obj.users_collection[0].name == space_obj.name
def test_multiple_assigns_do_not_create_duplicate_spatial_structure_collections(self):
space_obj = bpy.data.objects.new("IfcSpace/Name", None)
space_element = tool.Ifc.get().createIfcSpace()
tool.Ifc.link(space_element, space_obj)
bpy.context.scene.collection.objects.link(space_obj)
ifcopenshell.api.run(
"aggregate.assign_object",
tool.Ifc.get(),
relating_object=tool.Ifc.get().by_type("IfcSite")[0],
products=[space_element],
)
subject.assign(space_obj)
subject.assign(space_obj)
assert bpy.data.collections.get("IfcSpace/Name")
assert bpy.data.collections.get("IfcSite/My Site")
assert not bpy.data.collections.get("IfcSpace/Name.001")
assert not bpy.data.collections.get("IfcSite/My Site.001")
def test_spatial_zone_elements_are_not_placed_in_a_collection_of_the_same_name(self):
space_obj = bpy.data.objects.new("IfcSpaceZone/Name", None)
space_element = tool.Ifc.get().createIfcSpatialZone()
tool.Ifc.link(space_element, space_obj)
bpy.context.scene.collection.objects.link(space_obj)
ifcopenshell.api.run(
"aggregate.assign_object",
tool.Ifc.get(),
relating_object=tool.Ifc.get().by_type("IfcSite")[0],
products=[space_element],
)
subject.assign(space_obj)
assert len(space_obj.users_collection) == 2
assert space_obj.users_collection[0].name != space_obj.name
def test_aggregates_are_also_placed_in_their_container(self):
element_obj = bpy.data.objects.new("IfcElementAssembly/Name", None)
element = tool.Ifc.get().createIfcElementAssembly()
subelement_obj = bpy.data.objects.new("IfcBeam/Name", None)
subelement = tool.Ifc.get().createIfcBeam()
tool.Ifc.link(element, element_obj)
tool.Ifc.link(subelement, subelement_obj)
bpy.context.scene.collection.objects.link(element_obj)
bpy.context.scene.collection.objects.link(subelement_obj)
ifcopenshell.api.run(
"aggregate.assign_object",
tool.Ifc.get(),
relating_object=element,
products=[subelement],
)
ifcopenshell.api.run(
"spatial.assign_container",
tool.Ifc.get(),
products=[element],
relating_structure=tool.Ifc.get().by_type("IfcSite")[0],
)
subject.assign(element_obj)
subject.assign(subelement_obj)
assert len(element_obj.users_collection) == 2
assert len(subelement_obj.users_collection) == 2
assert "IfcSite" in element_obj.users_collection[0].name
assert "IfcSite" in subelement_obj.users_collection[0].name
def test_projects_are_placed_in_a_collection_of_the_same_name(self):
tool.Ifc.set(ifcopenshell.file())
element_obj = bpy.data.objects.new("IfcProject/Name", None)
element = tool.Ifc.get().createIfcProject()
tool.Ifc.link(element, element_obj)
bpy.context.scene.collection.objects.link(element_obj)
subject.assign(element_obj)
assert len(element_obj.users_collection) == 2
assert element_obj.users_collection[0].name == element_obj.name
def test_multiple_assigns_do_not_create_duplicate_collections(self):
tool.Ifc.set(ifcopenshell.file())
element_obj = bpy.data.objects.new("IfcProject/Name", None)
element = tool.Ifc.get().createIfcProject()
tool.Ifc.link(element, element_obj)
bpy.context.scene.collection.objects.link(element_obj)
subject.assign(element_obj)
subject.assign(element_obj)
assert bpy.data.collections.get("IfcProject/Name")
assert not bpy.data.collections.get("IfcProject/Name.001")
def test_own_collections_are_retained_and_name_synced(self):
space_obj = bpy.data.objects.new("IfcSpace/Name", None)
space_element = tool.Ifc.get().createIfcSpace()
tool.Ifc.link(space_element, space_obj)
space_collection = bpy.data.collections.new("Foobar")
bpy.context.scene.collection.children.link(space_collection)
space_obj.BIMObjectProperties.collection = space_collection
space_collection.objects.link(space_obj)
ifcopenshell.api.run(
"aggregate.assign_object",
tool.Ifc.get(),
relating_object=tool.Ifc.get().by_type("IfcSite")[0],
products=[space_element],
)
subject.assign(space_obj)
assert bpy.context.scene.collection.children.find(space_collection.name) != -1
assert bpy.data.collections.get("IfcSite/My Site").children.find(space_collection.name) == -1
assert bpy.data.collections.get("IfcProject/My Project").children.find(space_collection.name) == -1
assert bpy.context.scene.collection.children.find(space_collection.name) != -1
assert space_collection.objects.find(space_obj.name) != -1
assert space_collection.name == "IfcSpace/Name"
def test_types_are_placed_in_the_types_collection(self):
element_obj = bpy.data.objects.new("IfcWallType/Name", None)
element = tool.Ifc.get().createIfcWallType()
tool.Ifc.link(element, element_obj)
bpy.context.scene.collection.objects.link(element_obj)
subject.assign(element_obj)
assert element_obj.users_collection[0].name == "IfcTypeProduct"
assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcTypeProduct")
def test_openings_are_placed_in_the_openings_collection(self):
element_obj = bpy.data.objects.new("IfcOpeningElement/Name", None)
element = tool.Ifc.get().createIfcOpeningElement()
tool.Ifc.link(element, element_obj)
bpy.context.scene.collection.objects.link(element_obj)
subject.assign(element_obj)
assert element_obj.users_collection[0].name == "IfcOpeningElement"
assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcOpeningElement")
def test_grids_are_placed_in_their_container(self):
element_obj = bpy.data.objects.new("IfcGrid/Name", None)
element = tool.Ifc.get().createIfcGrid()
tool.Ifc.link(element, element_obj)
ifcopenshell.api.run(
"spatial.assign_container",
tool.Ifc.get(),
products=[element],
relating_structure=tool.Ifc.get().by_type("IfcSite")[0],
)
bpy.context.scene.collection.objects.link(element_obj)
subject.assign(element_obj)
assert element_obj.users_collection[0].name == "IfcSite/My Site"
def test_grids_axes_are_placed_in_the_grids_container(self):
element_obj = bpy.data.objects.new("IfcGrid/Name", None)
axis_obj = bpy.data.objects.new("IfcGrid/Name", None)
axis = tool.Ifc.get().createIfcGridAxis()
element = tool.Ifc.get().createIfcGrid(UAxes=[axis])
tool.Ifc.link(element, element_obj)
tool.Ifc.link(axis, axis_obj)
ifcopenshell.api.run(
"spatial.assign_container",
tool.Ifc.get(),
products=[element],
relating_structure=tool.Ifc.get().by_type("IfcSite")[0],
)
bpy.context.scene.collection.objects.link(element_obj)
subject.assign(element_obj)
subject.assign(axis_obj)
assert axis_obj.users_collection[0].name == "IfcSite/My Site"
def test_drawings_are_placed_in_their_own_collection(self):
element_obj = bpy.data.objects.new("IfcAnnotation/DRAWING", None)
element = tool.Ifc.get().createIfcAnnotation(ObjectType="DRAWING")
tool.Ifc.link(element, element_obj)
group = ifcopenshell.api.run("group.add_group", tool.Ifc.get())
group.ObjectType = "DRAWING"
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=[element], group=group)
subject.assign(element_obj)
assert element_obj.users_collection[0].name == "IfcAnnotation/DRAWING"
assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcAnnotation/DRAWING")
def test_annotations_are_placed_in_their_drawings_collection(self):
self.test_drawings_are_placed_in_their_own_collection()
ifc_file = tool.Ifc.get()
element_obj = bpy.data.objects.new("IfcAnnotation/Name", None)
element = ifc_file.createIfcAnnotation()
tool.Ifc.link(element, element_obj)
group = ifc_file.by_type("IfcGroup")[0]
ifcopenshell.api.run("group.assign_group", ifc_file, products=[element], group=group)
subject.assign(element_obj)
assert element_obj.users_collection[0].name == "IfcAnnotation/DRAWING"
def test_structural_members_are_placed_in_a_members_collection(self):
element_obj = bpy.data.objects.new("IfcStructuralCurveMember/Name", None)
element = tool.Ifc.get().createIfcStructuralCurveMember()
tool.Ifc.link(element, element_obj)
subject.assign(element_obj)
assert element_obj.users_collection[0].name == "IfcStructuralItem"
assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcStructuralItem")
def test_structural_connections_are_placed_in_a_connections_collection(self):
element_obj = bpy.data.objects.new("IfcStructuralCurveConnection/Name", None)
element = tool.Ifc.get().createIfcStructuralCurveConnection()
tool.Ifc.link(element, element_obj)
subject.assign(element_obj)
assert element_obj.users_collection[0].name == "IfcStructuralItem"
assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcStructuralItem")
class TestAssignIFC4X3(NewIfc4X3):
def test_linear_positioning_elements_are_placed_in_a_special_collection(self):
element_obj = bpy.data.objects.new("Name", None)
element = tool.Ifc.get().createIfcAlignment()
tool.Ifc.link(element, element_obj)
subject.assign(element_obj)
assert element_obj.users_collection[0].name == "IfcLinearPositioningElement"
assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcLinearPositioningElement")
def test_referents_are_placed_in_a_special_collection(self):
element_obj = bpy.data.objects.new("Name", None)
element = tool.Ifc.get().createIfcReferent()
tool.Ifc.link(element, element_obj)
subject.assign(element_obj)
assert element_obj.users_collection[0].name == "IfcReferent"
assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcReferent")
+119
View File
@@ -0,0 +1,119 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. 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.context import Context as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Context)
class TestSetContext(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
context = ifc.createIfcGeometricRepresentationContext()
subject.set_context(context)
assert bpy.context.scene.BIMContextProperties.active_context_id == context.id()
class TestImportAttributes(test.bim.bootstrap.NewFile):
def test_importing_a_context(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
context = ifc.createIfcGeometricRepresentationContext()
context.ContextIdentifier = "ContextIdentifier"
context.ContextType = "ContextType"
context.CoordinateSpaceDimension = 1
context.Precision = 1
subject.set_context(context)
subject.import_attributes()
props = bpy.context.scene.BIMContextProperties
assert props.context_attributes.get("ContextIdentifier").string_value == "ContextIdentifier"
assert props.context_attributes.get("ContextType").string_value == "ContextType"
assert props.context_attributes.get("CoordinateSpaceDimension").int_value == 1
assert props.context_attributes.get("Precision").float_value == 1
def test_importing_a_subcontext(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
subcontext = ifc.createIfcGeometricRepresentationSubcontext()
subcontext.TargetScale = 0.5
subcontext.TargetView = "NOTDEFINED"
subcontext.UserDefinedTargetView = "UserDefinedTargetView"
subject.set_context(subcontext)
subject.import_attributes()
props = bpy.context.scene.BIMContextProperties
assert props.context_attributes.get("TargetScale").float_value == 0.5
assert props.context_attributes.get("TargetView").enum_value == "NOTDEFINED"
assert props.context_attributes.get("UserDefinedTargetView").string_value == "UserDefinedTargetView"
assert not props.context_attributes.get("Precision")
def test_importing_twice(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
context = ifc.createIfcGeometricRepresentationContext()
context.ContextIdentifier = "ContextIdentifier"
subject.set_context(context)
subject.import_attributes()
context.ContextIdentifier = "ContextIdentifier2"
subject.import_attributes()
props = bpy.context.scene.BIMContextProperties
assert props.context_attributes.get("ContextIdentifier").string_value == "ContextIdentifier2"
class TestClearContext(test.bim.bootstrap.NewFile):
def test_run(self):
TestSetContext().test_run()
subject.clear_context()
assert bpy.context.scene.BIMContextProperties.active_context_id == 0
class TestGetContext(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
context = ifc.createIfcGeometricRepresentationContext()
subject.set_context(context)
assert subject.get_context() == context
class TestExportAttributes(test.bim.bootstrap.NewFile):
def test_exporting_a_context(self):
TestImportAttributes().test_importing_a_context()
assert subject.export_attributes() == {
"ContextIdentifier": "ContextIdentifier",
"ContextType": "ContextType",
"CoordinateSpaceDimension": 1,
"Precision": 1,
}
def test_exporting_a_subcontext(self):
TestImportAttributes().test_importing_a_subcontext()
assert subject.export_attributes() == {
"TargetScale": 0.5,
"TargetView": "NOTDEFINED",
"UserDefinedTargetView": "UserDefinedTargetView",
"ContextIdentifier": None,
"ContextType": None,
}
+61
View File
@@ -0,0 +1,61 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import os
import bpy
import ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.debug import Debug as subject
from blenderbim.bim.ifc import IfcStore
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Debug)
class TestAddSchemaIdentifier(NewFile):
def test_run(self):
cwd = os.path.dirname(os.path.realpath(__file__))
schema_path = os.path.join(cwd, "..", "files", "test.exp")
schema = subject.load_express(schema_path)
subject.add_schema_identifier(schema)
assert IfcStore.schema_identifiers[-1] == "IFCROGUE"
os.remove(schema_path + ".cache.dat")
class TestLoadExpress(NewFile):
def test_run(self):
cwd = os.path.dirname(os.path.realpath(__file__))
schema_path = os.path.join(cwd, "..", "files", "test.exp")
schema = subject.load_express(schema_path)
assert schema.schema_name == "IFCROGUE"
os.remove(schema_path + ".cache.dat")
class TestPurgeHdf5Cache(NewFile):
def test_run(self):
cache_dir = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache")
test_file = os.path.join(cache_dir, "test.h5")
if not os.path.isfile(test_file):
fp = open(test_file, "x")
fp.close()
subject.purge_hdf5_cache()
assert not [f for f in os.listdir(cache_dir) if f.endswith(".h5")]
+94
View File
@@ -0,0 +1,94 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
# ############################################################################ #
# Hey there! Welcome to the Bonsai code. Please feel free to reach
# out if you have any questions or need further guidance. Happy hacking!
# ############################################################################ #
# Note: these tests are commented out as they are for learning purposes only. If
# you want to run these tests, uncomment it :)
"""
# Because our tools have well defined, isolated functions, it means we can test
# them very easily in isolation. Tests are fun, fast, and easy to setup!
import bpy
import ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.demo import Demo as subject
# Our first test is that our tool implements all the abstract methods defined by
# the `core/tool.py` interface. Anytime the core wants something that the tools
# don't provide, this test will catch it. This type of test comes for free in
# other languages, but not Python, so we test it explicitly.
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Demo)
# These are fairly boring tests. What it does demonstrate is that no matter how
# complex a BIM application can get, it can always be reduced down to small,
# easily tested functions. Note that these functions actually are concrete
# implementations, so that means that you need to use Blender headlessly to run
# these tests.
class TestClearNameField(NewFile):
def test_run(self):
bpy.context.scene.BIMDemoProperties.name = "name"
subject.clear_name_field()
assert bpy.context.scene.BIMDemoProperties.name == ""
class TestGetProject(NewFile):
def test_run(self):
# Sometimes, there is a bit of preparation work to setup a scenario that
# can be tested. In this case, we need to set an active IFC dataset with
# a project. This is normal.
ifc = ifcopenshell.file()
project = ifc.createIfcProject()
tool.Ifc.set(ifc)
assert subject.get_project() == project
class TestHideUserHints(NewFile):
def test_run(self):
bpy.context.scene.BIMDemoProperties.show_hints = True
subject.hide_user_hints()
assert bpy.context.scene.BIMDemoProperties.show_hints == False
class TestSetMessage(NewFile):
def test_run(self):
bpy.context.scene.BIMDemoProperties.message = ""
subject.set_message("message")
assert bpy.context.scene.BIMDemoProperties.message == "message"
class TestShowUserHints(NewFile):
def test_run(self):
bpy.context.scene.BIMDemoProperties.show_hints = False
subject.show_user_hints()
assert bpy.context.scene.BIMDemoProperties.show_hints == True
"""
+235
View File
@@ -0,0 +1,235 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.api
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.document import Document as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Document)
class TestAddBreadcrumb(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
document = ifc.createIfcDocumentInformation()
subject.add_breadcrumb(document)
props = bpy.context.scene.BIMDocumentProperties
assert props.breadcrumbs[0].name == str(document.id())
class TestClearBreadcrumbs(NewFile):
def test_run(self):
props = bpy.context.scene.BIMDocumentProperties
props.breadcrumbs.add()
subject.clear_breadcrumbs()
assert len(props.breadcrumbs) == 0
class TestClearDocumentTree(NewFile):
def test_run(self):
props = bpy.context.scene.BIMDocumentProperties
new = props.documents.add()
subject.clear_document_tree()
assert len(props.documents) == 0
class TestDisableEditingDocument(NewFile):
def test_run(self):
bpy.context.scene.BIMDocumentProperties.active_document_id = 1
subject.disable_editing_document()
assert bpy.context.scene.BIMDocumentProperties.active_document_id == 0
class TestDisableEditingUI(NewFile):
def test_run(self):
bpy.context.scene.BIMDocumentProperties.is_editing = True
subject.disable_editing_ui()
assert bpy.context.scene.BIMDocumentProperties.is_editing == False
class TestEnableEditingUI(NewFile):
def test_run(self):
bpy.context.scene.BIMDocumentProperties.is_editing = False
subject.enable_editing_ui()
assert bpy.context.scene.BIMDocumentProperties.is_editing == True
class TestExportDocumentAttributes(NewFile):
def test_exporting_information(self):
TestImportDocumentAttributes().test_importing_information()
assert subject.export_document_attributes() == {
"Identification": "Identification",
"Name": "Name",
"Description": "Description",
"Location": "Location",
"Purpose": "Purpose",
"IntendedUse": "IntendedUse",
"Scope": "Scope",
"Revision": "Revision",
"CreationTime": "CreationTime",
"LastRevisionTime": "LastRevisionTime",
"ElectronicFormat": "ElectronicFormat",
"ValidFrom": "ValidFrom",
"ValidUntil": "ValidUntil",
"Confidentiality": "CONFIDENTIAL",
"Status": "DRAFT",
}
class TestGetActiveBreadcrumb(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
document = ifc.createIfcDocumentInformation()
subject.add_breadcrumb(document)
assert subject.get_active_breadcrumb() == document
class TestImportDocumentAttributes(NewFile):
def test_importing_information(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
document = ifc.createIfcDocumentInformation()
document.Identification = "Identification"
document.Name = "Name"
document.Description = "Description"
document.Location = "Location"
document.Purpose = "Purpose"
document.IntendedUse = "IntendedUse"
document.Scope = "Scope"
document.Revision = "Revision"
document.CreationTime = "CreationTime"
document.LastRevisionTime = "LastRevisionTime"
document.ElectronicFormat = "ElectronicFormat"
document.ValidFrom = "ValidFrom"
document.ValidUntil = "ValidUntil"
document.Confidentiality = "CONFIDENTIAL"
document.Status = "DRAFT"
subject().import_document_attributes(document)
props = bpy.context.scene.BIMDocumentProperties
assert props.document_attributes.get("Identification").string_value == "Identification"
assert props.document_attributes.get("Name").string_value == "Name"
assert props.document_attributes.get("Description").string_value == "Description"
assert props.document_attributes.get("Location").string_value == "Location"
assert props.document_attributes.get("Purpose").string_value == "Purpose"
assert props.document_attributes.get("IntendedUse").string_value == "IntendedUse"
assert props.document_attributes.get("Scope").string_value == "Scope"
assert props.document_attributes.get("Revision").string_value == "Revision"
assert props.document_attributes.get("CreationTime").string_value == "CreationTime"
assert props.document_attributes.get("LastRevisionTime").string_value == "LastRevisionTime"
assert props.document_attributes.get("ElectronicFormat").string_value == "ElectronicFormat"
assert props.document_attributes.get("ValidFrom").string_value == "ValidFrom"
assert props.document_attributes.get("ValidUntil").string_value == "ValidUntil"
assert props.document_attributes.get("Confidentiality").enum_value == "CONFIDENTIAL"
assert props.document_attributes.get("Status").enum_value == "DRAFT"
def test_importing_reference(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
document = ifc.createIfcDocumentInformation()
document.Location = "Location"
document.Identification = "Identification"
document.Name = "Name"
document.Description = "Description"
subject().import_document_attributes(document)
props = bpy.context.scene.BIMDocumentProperties
assert props.document_attributes.get("Location").string_value == "Location"
assert props.document_attributes.get("Identification").string_value == "Identification"
assert props.document_attributes.get("Name").string_value == "Name"
assert props.document_attributes.get("Description").string_value == "Description"
class TestImportProjectDocuments(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
ifc.createIfcProject()
document = ifcopenshell.api.run("document.add_information", ifc)
subject.import_project_documents()
props = bpy.context.scene.BIMDocumentProperties
assert len(props.documents) == 1
assert props.documents[0].ifc_definition_id == document.id()
assert props.documents[0].name == "Unnamed"
assert props.documents[0].identification == "X"
assert props.documents[0].is_information is True
class TestImportReferences(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
ifc.createIfcProject()
document = ifcopenshell.api.run("document.add_information", ifc)
reference = ifcopenshell.api.run("document.add_reference", ifc, information=document)
subject.import_references(document)
props = bpy.context.scene.BIMDocumentProperties
assert len(props.documents) == 1
assert props.documents[0].ifc_definition_id == reference.id()
assert props.documents[0].name == "Unnamed"
assert props.documents[0].identification == "X"
assert props.documents[0].is_information is False
class TestImportSubdocuments(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
ifc.createIfcProject()
document = ifcopenshell.api.run("document.add_information", ifc)
subdocument = ifcopenshell.api.run("document.add_information", ifc, parent=document)
subject.import_subdocuments(document)
props = bpy.context.scene.BIMDocumentProperties
assert len(props.documents) == 1
assert props.documents[0].ifc_definition_id == subdocument.id()
assert props.documents[0].name == "Unnamed"
assert props.documents[0].identification == "X"
assert props.documents[0].is_information is True
class TestIsDocumentInformation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
information = ifc.createIfcDocumentInformation()
reference = ifc.createIfcDocumentReference()
assert subject.is_document_information(information) is True
assert subject.is_document_information(reference) is False
class TestRemoveLatestBreadcrumb(NewFile):
def test_run(self):
props = bpy.context.scene.BIMDocumentProperties
props.breadcrumbs.add()
props.breadcrumbs.add()
subject.remove_latest_breadcrumb()
assert len(props.breadcrumbs) == 1
class TestSetActiveDocument(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
document = ifc.createIfcDocumentInformation()
subject.set_active_document(document)
assert bpy.context.scene.BIMDocumentProperties.active_document_id == document.id()
+882
View File
@@ -0,0 +1,882 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import os
from pathlib import Path
import bpy
import mathutils
import ifcopenshell
import ifcopenshell.guid
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.drawing import Drawing as subject
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.drawing.data import DecoratorData
from mathutils import Vector
import xml.etree.ElementTree as ET
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Drawing)
class TestCopyRepresentation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
source = ifc.createIfcAnnotation(Representation=ifc.createIfcProductDefinitionShape())
dest = ifc.createIfcAnnotation()
subject.copy_representation(source, dest)
assert dest.Representation.is_a("IfcProductDefinitionShape")
class TestCreateAnnotationObject(NewFile):
def test_nothing(self):
pass
class TestCreateCamera(NewFile):
def test_run(self):
obj = subject.create_camera("Name", mathutils.Matrix(), "PERSPECTIVE")
assert obj.name == "Name"
assert obj.matrix_world == mathutils.Matrix()
assert obj.data.type == "PERSP"
assert obj.data.ortho_scale == 50
assert obj.data.clip_end == 10
assert obj.users_collection[0] == bpy.context.scene.collection
class TestCreateSvgSheet(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
tool.Ifc.set(ifc)
document = ifc.createIfcDocumentInformation(
Identification="X",
Name="FOOBAR",
Scope="DOCUMENTATION",
Location=os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", "X - FOOBAR.svg"),
)
uri = subject.create_svg_sheet(document, "A1")
assert uri.endswith(".svg")
assert os.path.isfile(uri)
class TestDeleteCollection(NewFile):
def test_run(self):
collection = bpy.data.collections.new("Foobar")
subject.delete_collection(collection)
assert not bpy.data.collections.get("Foobar")
class TestDeleteDrawingElements(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
collection = bpy.data.collections.new("Collection")
bpy.context.scene.collection.children.link(collection)
collection.objects.link(obj)
element = ifc.createIfcAnnotation(GlobalId=ifcopenshell.guid.new())
tool.Ifc.link(element, obj)
element_id = element.id()
subject.delete_drawing_elements([element])
try:
ifc.by_id(element_id)
assert False
except:
pass
assert not bpy.data.objects.get("Object")
class TestDisableEditingDrawings(NewFile):
def test_run(self):
bpy.context.scene.DocProperties.is_editing_drawings = True
subject.disable_editing_drawings()
assert bpy.context.scene.DocProperties.is_editing_drawings == False
class TestDisableEditingSchedules(NewFile):
def test_run(self):
bpy.context.scene.DocProperties.is_editing_schedules = True
subject.disable_editing_schedules()
assert bpy.context.scene.DocProperties.is_editing_schedules == False
class TestDisableEditingReferences(NewFile):
def test_run(self):
bpy.context.scene.DocProperties.is_editing_references = True
subject.disable_editing_references()
assert bpy.context.scene.DocProperties.is_editing_references == False
class TestDisableEditingSheets(NewFile):
def test_run(self):
bpy.context.scene.DocProperties.is_editing_sheets = True
subject.disable_editing_sheets()
assert bpy.context.scene.DocProperties.is_editing_sheets == False
class TestDisableEditingText(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
obj.BIMTextProperties.is_editing = True
subject.disable_editing_text(obj)
assert obj.BIMTextProperties.is_editing == False
class TestDisableEditingAssignedProduct(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
obj.BIMAssignedProductProperties.is_editing_product = True
subject.disable_editing_assigned_product(obj)
assert obj.BIMAssignedProductProperties.is_editing_product == False
class TestEnableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
bpy.context.scene.collection.objects.link(obj)
subject.enable_editing(obj)
assert obj in bpy.context.selected_objects
class TestEnableEditingDrawings(NewFile):
def test_run(self):
bpy.context.scene.DocProperties.is_editing_drawings = False
subject.enable_editing_drawings()
assert bpy.context.scene.DocProperties.is_editing_drawings == True
class TestEnableEditingSchedules(NewFile):
def test_run(self):
bpy.context.scene.DocProperties.is_editing_schedules = False
subject.enable_editing_schedules()
assert bpy.context.scene.DocProperties.is_editing_schedules == True
class TestEnableEditingReferences(NewFile):
def test_run(self):
bpy.context.scene.DocProperties.is_editing_references = False
subject.enable_editing_references()
assert bpy.context.scene.DocProperties.is_editing_references == True
class TestEnableEditingSheets(NewFile):
def test_run(self):
bpy.context.scene.DocProperties.is_editing_sheets = False
subject.enable_editing_sheets()
assert bpy.context.scene.DocProperties.is_editing_sheets == True
class TestEnableEditingText(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing_text(obj)
assert obj.BIMTextProperties.is_editing == True
class TestEnableEditingAssignedProduct(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing_assigned_product(obj)
assert obj.BIMAssignedProductProperties.is_editing_product == True
class TestEnsureUniqueDrawingName(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
assert subject.ensure_unique_drawing_name("FOOBAR") == "FOOBAR"
ifc.createIfcAnnotation(Name="FOOBAR", ObjectType="DRAWING")
assert subject.ensure_unique_drawing_name("FOOBAR") == "FOOBAR-X"
ifc.createIfcAnnotation(Name="FOOBAR-X", ObjectType="DRAWING")
assert subject.ensure_unique_drawing_name("FOOBAR") == "FOOBAR-X-X"
class TestEnsureUniqueIdentification(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
assert subject.ensure_unique_identification("FOOBAR") == "FOOBAR"
ifc.createIfcDocumentInformation(Identification="FOOBAR", Scope="DOCUMENTATION")
assert subject.ensure_unique_identification("FOOBAR") == "FOOBAR-X"
ifc.createIfcDocumentInformation(Identification="FOOBAR-X", Scope="DOCUMENTATION")
assert subject.ensure_unique_identification("FOOBAR") == "FOOBAR-X-X"
def test_unique_document_id_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
assert subject.ensure_unique_identification("FOOBAR") == "FOOBAR"
ifc.createIfcDocumentInformation(DocumentId="FOOBAR", Scope="DOCUMENTATION")
assert subject.ensure_unique_identification("FOOBAR") == "FOOBAR-X"
ifc.createIfcDocumentInformation(DocumentId="FOOBAR-X", Scope="DOCUMENTATION")
assert subject.ensure_unique_identification("FOOBAR") == "FOOBAR-X-X"
class TestExportTextLiteralAttributes(NewFile):
def test_run(self):
TestImportTextAttributes().test_run()
assert subject.export_text_literal_attributes(bpy.data.objects.get("Object")) == [
{
"Literal": "Literal",
"Path": "RIGHT",
"BoxAlignment": "bottom-left",
}
]
class TestGetAnnotationContext(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
context = ifc.createIfcGeometricRepresentationSubContext(
ContextType="Plan", ContextIdentifier="Annotation", TargetView="PLAN_VIEW"
)
context2 = ifc.createIfcGeometricRepresentationSubContext(
ContextType="Model", ContextIdentifier="Annotation", TargetView="ELEVATION_VIEW"
)
context3 = ifc.createIfcGeometricRepresentationSubContext(
ContextType="Model", ContextIdentifier="Annotation", TargetView="PLAN_VIEW"
)
tool.Ifc.set(ifc)
assert subject.get_annotation_context("PLAN_VIEW") == context
assert subject.get_annotation_context("ELEVATION_VIEW") == context2
assert subject.get_annotation_context("PLAN_VIEW", "FALL") == context3
class TestGetBodyContext(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
context = ifc.createIfcGeometricRepresentationSubContext(
ContextType="Model", ContextIdentifier="Body", TargetView="MODEL_VIEW"
)
tool.Ifc.set(ifc)
assert subject.get_body_context() == context
class TestGetDocumentUri(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
document = ifc.createIfcDocumentInformation(
Identification="X", Name="FOOBAR", Scope="SHEET", Location="Location"
)
assert subject.get_document_uri(document) == os.path.abspath(os.path.join(tool.Ifc.get_path(), "Location"))
def test_get_indirect_locations(self):
ifc = ifcopenshell.file()
document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="SHEET")
reference = ifc.createIfcDocumentReference(Location="Location", ReferencedDocument=document)
assert subject.get_document_uri(document) == os.path.abspath(os.path.join(tool.Ifc.get_path(), "Location"))
assert subject.get_document_uri(reference) == os.path.abspath(os.path.join(tool.Ifc.get_path(), "Location"))
def test_run_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
reference = ifc.createIfcDocumentReference(Location="Location")
document = ifc.createIfcDocumentInformation(
DocumentId="X", Name="FOOBAR", Scope="SHEET", DocumentReferences=[reference]
)
assert subject.get_document_uri(document) == os.path.abspath(os.path.join(tool.Ifc.get_path(), "Location"))
assert subject.get_document_uri(reference) == os.path.abspath(os.path.join(tool.Ifc.get_path(), "Location"))
class TestGetDrawingCollection(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", None)
collection = bpy.data.collections.new("Collection")
bpy.context.scene.collection.children.link(collection)
collection.objects.link(obj)
obj.BIMObjectProperties.collection = collection
collection.BIMCollectionProperties.obj = obj
element = ifc.createIfcAnnotation()
tool.Ifc.link(element, obj)
assert subject.get_drawing_collection(element) == collection
class TestGetDrawingGroup(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcAnnotation()
group = ifcopenshell.api.run("group.add_group", ifc)
group.ObjectType = "DRAWING"
ifcopenshell.api.run("group.assign_group", ifc, products=[element], group=group)
assert subject.get_drawing_group(element) == group
class TestGetDrawingTargetView(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcAnnotation()
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="EPset_Drawing")
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"TargetView": "PLAN_VIEW"})
assert subject.get_drawing_target_view(element) == "PLAN_VIEW"
class TestGetGroupElements(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcAnnotation()
group = ifcopenshell.api.run("group.add_group", ifc)
ifcopenshell.api.run("group.assign_group", ifc, products=[element], group=group)
assert subject.get_group_elements(group) == (element,)
class TestGetIfcRepresentationClass(NewFile):
def test_run(self):
assert subject.get_ifc_representation_class("TEXT") == "IfcTextLiteral"
assert subject.get_ifc_representation_class("TEXT_LEADER") == "IfcGeometricCurveSet/IfcTextLiteral"
assert subject.get_ifc_representation_class("FOOBAR") == ""
class TestGetName(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
assert subject.get_name(ifc.createIfcWall(Name="Foobar")) == "Foobar"
class TestGenerateDrawingMatrix(NewFile):
def test_returning_the_origin_as_a_fallback(self):
assert subject.generate_drawing_matrix("PLAN_VIEW", None) == mathutils.Matrix()
def test_creating_a_plan_view_at_the_cursor_at_a_storey(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", None)
element = ifc.createIfcBuildingStorey()
tool.Ifc.link(element, obj)
bpy.context.scene.collection.objects.link(obj)
obj.matrix_world[2][3] = 3
bpy.context.scene.cursor.location = (1.0, 2.0, 0.0)
m = subject.generate_drawing_matrix("PLAN_VIEW", element.id())
assert round(m[0][3], 3) == 1
assert round(m[1][3], 3) == 2
assert round(m[2][3], 3) == 4.6
def test_creating_an_rcp_at_the_origin(self):
assert subject.generate_drawing_matrix("REFLECTED_PLAN_VIEW", None) == mathutils.Matrix(
((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, -1, 0), (0, 0, 0, 1))
)
def test_creating_an_rcp_at_the_cursor_at_a_storey(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", None)
element = ifc.createIfcBuildingStorey()
tool.Ifc.link(element, obj)
bpy.context.scene.collection.objects.link(obj)
obj.matrix_world[2][3] = 3
bpy.context.scene.cursor.location = (1.0, 2.0, 0.0)
assert subject.generate_drawing_matrix("REFLECTED_PLAN_VIEW", element.id()) == mathutils.Matrix(
((1, 0, 0, 1), (0, 1, 0, 2), (0, 0, -1, 3 + 1.6), (0, 0, 0, 1))
)
def test_creating_a_north_elevation_at_the_cursor(self):
bpy.context.scene.cursor.location = (1.0, 2.0, 3.0)
assert subject.generate_drawing_matrix("ELEVATION_VIEW", "NORTH") == mathutils.Matrix(
((-1, 0, 0, 1), (0, 0, 1, 2), (0, 1, 0, 3), (0, 0, 0, 1))
)
def test_creating_a_south_elevation_at_the_cursor(self):
bpy.context.scene.cursor.location = (1.0, 2.0, 3.0)
assert subject.generate_drawing_matrix("ELEVATION_VIEW", "SOUTH") == mathutils.Matrix(
((1, 0, 0, 1), (0, 0, -1, 2), (0, 1, 0, 3), (0, 0, 0, 1))
)
def test_creating_an_east_elevation_at_the_cursor(self):
bpy.context.scene.cursor.location = (1.0, 2.0, 3.0)
assert subject.generate_drawing_matrix("ELEVATION_VIEW", "EAST") == mathutils.Matrix(
((0, 0, 1, 1), (1, 0, 0, 2), (0, 1, 0, 3), (0, 0, 0, 1))
)
def test_creating_a_west_elevation_at_the_cursor(self):
bpy.context.scene.cursor.location = (1.0, 2.0, 3.0)
assert subject.generate_drawing_matrix("ELEVATION_VIEW", "WEST") == mathutils.Matrix(
((0, 0, -1, 1), (-1, 0, 0, 2), (0, 1, 0, 3), (0, 0, 0, 1))
)
def test_creating_a_north_section_at_the_cursor(self):
bpy.context.scene.cursor.location = (1.0, 2.0, 3.0)
assert subject.generate_drawing_matrix("SECTION_VIEW", "NORTH") == mathutils.Matrix(
((1, 0, 0, 1), (0, 0, -1, 2), (0, 1, 0, 3), (0, 0, 0, 1))
)
def test_creating_a_south_section_at_the_cursor(self):
bpy.context.scene.cursor.location = (1.0, 2.0, 3.0)
assert subject.generate_drawing_matrix("SECTION_VIEW", "SOUTH") == mathutils.Matrix(
((-1, 0, 0, 1), (0, 0, 1, 2), (0, 1, 0, 3), (0, 0, 0, 1))
)
def test_creating_an_east_section_at_the_cursor(self):
bpy.context.scene.cursor.location = (1.0, 2.0, 3.0)
assert subject.generate_drawing_matrix("SECTION_VIEW", "EAST") == mathutils.Matrix(
((0, 0, -1, 1), (-1, 0, 0, 2), (0, 1, 0, 3), (0, 0, 0, 1))
)
def test_creating_a_west_section_at_the_cursor(self):
bpy.context.scene.cursor.location = (1.0, 2.0, 3.0)
assert subject.generate_drawing_matrix("SECTION_VIEW", "WEST") == mathutils.Matrix(
((0, 0, 1, 1), (1, 0, 0, 2), (0, 1, 0, 3), (0, 0, 0, 1))
)
class TestGenerateSheetIdentification(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
subject.generate_sheet_identification() == "A01"
document = ifc.createIfcDocumentInformation()
subject.generate_sheet_identification() == "A02"
class TestGetTextLiteral(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", None)
element = ifc.createIfcAnnotation()
element.Representation = ifc.createIfcProductDefinitionShape()
context = ifc.createIfcGeometricRepresentationSubContext(ContextType="Plan", ContextIdentifier="Annotation")
item = ifc.createIfcTextLiteralWithExtent(Literal="Literal", Path="RIGHT", BoxAlignment="bottom-left")
representation = ifc.createIfcShapeRepresentation(ContextOfItems=context, Items=[item])
element.Representation.Representations = [representation]
element.ObjectType = "TEXT" # TODO: double check if it's valid to set this
tool.Ifc.link(element, obj)
assert subject.get_text_literal(obj) == item
class TestGetAssignedProduct(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
wall = ifc.createIfcWall()
label = ifc.createIfcAnnotation()
ifcopenshell.api.run("drawing.assign_product", ifc, relating_product=wall, related_object=label)
assert subject.get_assigned_product(label) == wall
class TestImportDrawings(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
drawing = ifc.createIfcAnnotation(Name="FOOBAR", ObjectType="DRAWING")
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=drawing, name="EPset_Drawing")
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"TargetView": "PLAN_VIEW"})
subject.import_drawings()
props = bpy.context.scene.DocProperties
for d in props.drawings:
d.is_expanded = True
subject.import_drawings()
assert props.drawings[1].target_view == "PLAN_VIEW"
assert props.drawings[2].ifc_definition_id == drawing.id()
assert props.drawings[2].name == "FOOBAR"
class TestImportSchedules(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifc.createIfcDocumentInformation(Identification="Y", Name="FOOBAZ")
document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="SCHEDULE")
subject.import_documents("SCHEDULE")
props = bpy.context.scene.DocProperties
assert props.schedules[0].ifc_definition_id == document.id()
assert props.schedules[0].identification == "X"
assert props.schedules[0].name == "FOOBAR"
def test_run_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
ifc.createIfcDocumentInformation(DocumentId="Y", Name="FOOBAZ")
document = ifc.createIfcDocumentInformation(DocumentId="X", Name="FOOBAR", Scope="SCHEDULE")
subject.import_documents("SCHEDULE")
props = bpy.context.scene.DocProperties
assert props.schedules[0].ifc_definition_id == document.id()
assert props.schedules[0].identification == "X"
assert props.schedules[0].name == "FOOBAR"
class TestImportReferences(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifc.createIfcDocumentInformation(Identification="Y", Name="FOOBAZ")
document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="REFERENCE")
subject.import_documents("REFERENCE")
props = bpy.context.scene.DocProperties
assert props.references[0].ifc_definition_id == document.id()
assert props.references[0].identification == "X"
assert props.references[0].name == "FOOBAR"
def test_run_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
ifc.createIfcDocumentInformation(DocumentId="Y", Name="FOOBAZ")
document = ifc.createIfcDocumentInformation(DocumentId="X", Name="FOOBAR", Scope="REFERENCE")
subject.import_documents("REFERENCE")
props = bpy.context.scene.DocProperties
assert props.references[0].ifc_definition_id == document.id()
assert props.references[0].identification == "X"
assert props.references[0].name == "FOOBAR"
class TestImportSheets(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifc.createIfcDocumentInformation(Identification="Y", Name="FOOBAZ")
document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="SHEET")
subject.import_sheets()
props = bpy.context.scene.DocProperties
assert props.sheets[0].ifc_definition_id == document.id()
assert props.sheets[0].identification == "X"
assert props.sheets[0].name == "FOOBAR"
def test_run_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
ifc.createIfcDocumentInformation(DocumentId="Y", Name="FOOBAZ")
document = ifc.createIfcDocumentInformation(DocumentId="X", Name="FOOBAR", Scope="SHEET")
subject.import_sheets()
props = bpy.context.scene.DocProperties
assert props.sheets[0].ifc_definition_id == document.id()
assert props.sheets[0].identification == "X"
assert props.sheets[0].name == "FOOBAR"
class TestImportTextAttributes(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", None)
element = ifc.createIfcAnnotation()
element.Representation = ifc.createIfcProductDefinitionShape()
context = ifc.createIfcGeometricRepresentationSubContext(ContextType="Plan", ContextIdentifier="Annotation")
item = ifc.createIfcTextLiteralWithExtent(Literal="Literal", Path="RIGHT", BoxAlignment="bottom-left")
representation = ifc.createIfcShapeRepresentation(ContextOfItems=context, Items=[item])
element.Representation.Representations = [representation]
element.ObjectType = "TEXT" # TODO: double check if it's valid to set this
tool.Ifc.link(element, obj)
subject.import_text_attributes(obj)
literal_props = obj.BIMTextProperties.literals[0]
assert literal_props.attributes.get("Literal").string_value == "Literal"
assert literal_props.attributes.get("Path").enum_value == "RIGHT"
assert literal_props.attributes.get("BoxAlignment").string_value == "bottom-left"
class TestImportAssignedProduct(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
wall = ifc.createIfcWall()
label = ifc.createIfcAnnotation()
ifcopenshell.api.run("drawing.assign_product", ifc, relating_product=wall, related_object=label)
wall_obj = bpy.data.objects.new("Object", None)
label_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(wall, wall_obj)
tool.Ifc.link(label, label_obj)
subject.import_assigned_product(label_obj)
assert label_obj.BIMAssignedProductProperties.relating_product == wall_obj
def test_doing_nothing_if_no_product_to_import(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
label = ifc.createIfcAnnotation()
label_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(label, label_obj)
subject.import_assigned_product(label_obj)
assert label_obj.BIMAssignedProductProperties.relating_product is None
class TestOpenSchedule(NewFile):
def open_spreadsheet(self):
pass
class TestOpenReference(NewFile):
def open_svg(self):
pass
class TestOpenSvg(NewFile):
def test_nothing(self):
pass
class TestRunRootAssignClassOperator(NewFile):
def test_nothing(self):
pass
class TestSetDrawingCollectionName(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
group = ifc.createIfcGroup(Name="Foobaz")
collection = bpy.data.collections.new("Foobar")
subject.set_drawing_collection_name(group, collection)
assert collection.name == "IfcGroup/Foobaz"
class TestSetName(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
drawing = ifc.createIfcAnnotation()
subject.set_name(drawing, "Name")
assert drawing.Name == "Name"
class TestShowDecorations(NewFile):
def test_run(self):
bpy.context.scene.DocProperties.should_draw_decorations = False
subject.show_decorations()
assert bpy.context.scene.DocProperties.should_draw_decorations is True
class TestDrawingMaintainingSheetPosition(NewFile):
def get_sheet_drawing_data(self, layout_path):
SVG = "{http://www.w3.org/2000/svg}"
ET.register_namespace("", SVG)
layout_tree = ET.parse(layout_path)
layout_root = layout_tree.getroot()
drawing_view = layout_root.findall(f'{SVG}g[@data-type="drawing"]')[0]
drawing_data = {}
for image in drawing_view.findall(f"{SVG}image"):
attribs = ["x", "y", "width", "height"]
image_type = image.attrib["data-type"]
drawing_data[image_type] = tuple([round(float(image.attrib[attr]), 2) for attr in attribs])
return drawing_data
def test_run(self):
props = bpy.context.scene.DocProperties
bpy.ops.bim.create_project()
ifc = tool.Ifc.get()
sheet_path = Path.cwd() / "layouts" / "A00 - UNTITLED.svg"
bpy.ops.mesh.primitive_cube_add(size=10, location=(0, 0, 4))
obj = bpy.data.objects["Cube"]
bpy.ops.bim.assign_class(ifc_class="IfcActuator", predefined_type="ELECTRICACTUATOR", userdefined_type="")
bpy.ops.bim.load_sheets()
bpy.ops.bim.add_sheet()
bpy.ops.bim.load_drawings()
for d in props.drawings:
d.is_expanded = True
bpy.ops.bim.add_drawing()
drawing = ifc.by_type("IfcAnnotation")[0]
for i, d in enumerate(props.drawings):
if d.ifc_definition_id == drawing.id():
props.active_drawing_index = i
bpy.ops.bim.activate_drawing(drawing=drawing.id())
bpy.ops.bim.create_drawing()
bpy.ops.bim.add_drawing_to_sheet()
bpy.ops.bim.open_sheet()
# uncomment if debugging
# without `sleep` `bim.open_sheet` tend to open sheet in browser
# with PLAN_VIEW.svg that will be created only later
# from time import sleep
# sleep(0.1)
# check drawing default position
drawing_data = self.get_sheet_drawing_data(sheet_path)
assert drawing_data["foreground"] == (30.0, 30.0, 500.0, 500.0)
assert drawing_data["view-title"] == (30.0, 535.0, 50.22, 10.0)
bpy.context.scene.camera.data.BIMCameraProperties.width = 25
bpy.context.scene.camera.data.BIMCameraProperties.height = 25
tool.Blender.force_depsgraph_update()
bpy.ops.bim.create_drawing()
bpy.ops.bim.open_sheet()
assert sheet_path.is_file(), f"Sheet path {sheet_path} doesn't exist"
# check drawing position on the sheet
drawing_data = self.get_sheet_drawing_data(sheet_path)
assert drawing_data["foreground"] == (155.0, 155.0, 250.0, 250.0)
assert drawing_data["view-title"] == (155.0, 410.0, 50.22, 10.0)
class TestUpdateTextValue(NewFile):
def test_updating_arbitrary_strings(self):
TestGetTextLiteral().test_run()
ifc = tool.Ifc.get()
obj = bpy.data.objects.get("Object")
subject.update_text_value(obj)
literal = obj.BIMTextProperties.literals[0]
assert obj.BIMTextProperties.font_size == "2.5"
assert literal.value == "Literal"
assert literal.box_alignment[:] == tuple([False] * 6 + [True] + [False] * 2)
assert literal.ifc_definition_id == ifc.by_type("IfcTextLiteralWithExtent")[0].id()
def test_using_attribute_variables(self):
TestGetTextLiteral().test_run()
obj = bpy.data.objects.get("Object")
ifc = tool.Ifc.get()
wall = ifc.createIfcWall(Name="Baz")
label = ifc.by_type("IfcAnnotation")[0]
ifcopenshell.api.run("drawing.assign_product", ifc, relating_product=wall, related_object=label)
ifc.by_type("IfcTextLiteralWithExtent")[0].Literal = "Foo {{Name}} Bar"
subject.update_text_value(obj)
assert obj.BIMTextProperties.literals[0].value == "Foo Baz Bar"
def test_using_property_variables(self):
TestGetTextLiteral().test_run()
obj = bpy.data.objects.get("Object")
ifc = tool.Ifc.get()
wall = ifc.createIfcWall()
pset = ifcopenshell.api.run("pset.add_pset", ifc, name="Custom_Pset", product=wall)
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Key": "Baz"})
label = ifc.by_type("IfcAnnotation")[0]
ifcopenshell.api.run("drawing.assign_product", ifc, relating_product=wall, related_object=label)
ifc.by_type("IfcTextLiteralWithExtent")[0].Literal = "Foo {{Custom_Pset.Key}} Bar"
subject.update_text_value(obj)
assert obj.BIMTextProperties.literals[0].value == "Foo Baz Bar"
def test_update_text_font_size(self):
TestGetTextLiteral().test_run()
obj = bpy.data.objects.get("Object")
with bpy.context.temp_override(active_object=obj):
bpy.ops.bim.enable_editing_text()
obj.BIMTextProperties.font_size = "7.0"
bpy.ops.bim.edit_text()
annotation_classes = ifcopenshell.util.element.get_pset(tool.Ifc.get_entity(obj), "EPset_Annotation", "Classes")
assert "title" in annotation_classes
assert DecoratorData.get_ifc_text_data(obj)["FontSize"] == 7.0
def test_add_second_literal(self, setup=True):
if setup:
TestGetTextLiteral().test_run()
obj = bpy.data.objects.get("Object")
with bpy.context.temp_override(active_object=obj):
bpy.ops.bim.enable_editing_text()
bpy.ops.bim.add_text_literal()
literal = obj.BIMTextProperties.literals[1]
literal.attributes["Literal"].string_value = "test_value"
bpy.ops.bim.edit_text()
ifc = tool.Ifc.get()
assert ifc.by_type("IfcTextLiteralWithExtent")[1].Literal == "test_value"
def test_disable_text_editing(self):
# add second literal and change font size to test changing them
self.test_update_text_font_size() # sets font size to "7.0"
self.test_add_second_literal(setup=False)
obj = bpy.data.objects.get("Object")
props = obj.BIMTextProperties
assert obj is not None, obj
with bpy.context.temp_override(active_object=obj):
bpy.ops.bim.enable_editing_text()
bpy.ops.bim.remove_text_literal(literal_prop_id=1)
props.literals[0].attributes["Literal"].string_value = "changed_value"
props.font_size = "2.5"
bpy.ops.bim.disable_editing_text()
ifc = tool.Ifc.get()
# test font size
annotation_classes = ifcopenshell.util.element.get_pset(tool.Ifc.get_entity(obj), "EPset_Annotation", "Classes")
assert props.font_size == "7.0"
assert "title" in annotation_classes
assert DecoratorData.get_ifc_text_data(obj)["FontSize"] == 7.0
# test second literal is present
assert props.literals[1].attributes["Literal"].string_value == "test_value"
assert ifc.by_type("IfcTextLiteralWithExtent")[1].Literal == "test_value"
# test first literal value is unchanged
assert props.literals[0].attributes["Literal"].string_value == "Literal"
assert ifc.by_type("IfcTextLiteralWithExtent")[0].Literal == "Literal"
class TestDrawingStyles(NewFile):
def setup_project_with_drawing(self):
bpy.ops.bim.create_project()
bpy.ops.bim.load_drawings()
bpy.ops.bim.add_drawing()
ifc = tool.Ifc.get()
drawing = ifc.by_type("IfcAnnotation")[0]
bpy.ops.bim.activate_drawing(drawing=drawing.id())
self.drawing_styles = bpy.context.scene.DocProperties.drawing_styles
def test_drawing_styles_not_loaded_if_underlay_is_inactive(self):
self.setup_project_with_drawing()
assert len(self.drawing_styles) == 0
def test_drawing_styles_loaded_on_underlay_enabled(self):
self.setup_project_with_drawing()
bpy.context.scene.camera.data.BIMCameraProperties.has_underlay = True
assert len(self.drawing_styles) == 3
def test_drawing_styles_reload(self):
self.setup_project_with_drawing()
bpy.ops.bim.reload_drawing_styles()
assert len(self.drawing_styles) == 3
class TestAddReferenceImage(NewFile):
def test_run(self):
bpy.context.scene.BIMProjectProperties.template_file = "0"
bpy.ops.bim.create_project()
ifc_file = tool.Ifc.get()
filepath = Path("test/files/image.jpg").absolute()
bpy.ops.bim.add_reference_image(filepath=str(filepath))
obj = bpy.data.objects["IfcAnnotation/image"]
assert obj is not None
assert tool.Cad.are_vectors_equal(obj.dimensions, Vector((3.53982, 2.0, 0.0)))
material = obj.active_material
assert material.name == "image"
assert material.BIMStyleProperties.ifc_definition_id != 0
style = ifc_file.by_id(material.BIMStyleProperties.ifc_definition_id)
styled_items = set(tool.Style.get_styled_items(style))
representation_items = set(tool.Geometry.get_active_representation(obj).Items)
assert styled_items == representation_items
material_nodes = material.node_tree.nodes
texture_filepath = material_nodes["Image Texture"].image.filepath
texture_filepath = Path(tool.Blender.blender_path_to_posix(texture_filepath))
assert texture_filepath == filepath
uv_node = material_nodes["Texture Coordinate"]
assert len(uv_node.outputs["Generated"].links[:]) == 1
+758
View File
@@ -0,0 +1,758 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import math
import numpy as np
import ifcopenshell
import ifcopenshell.api.type
import blenderbim.core.tool
import blenderbim.tool as tool
from mathutils import Vector
from test.bim.bootstrap import NewFile
from blenderbim.tool.geometry import Geometry as subject
from typing import Union
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Geometry)
class TestChangeObjectData(NewFile):
def test_change_single_object_data(self):
data1 = bpy.data.meshes.new("Mesh")
data2 = bpy.data.meshes.new("Mesh")
obj1 = bpy.data.objects.new("Object", data1)
obj2 = bpy.data.objects.new("Object", data1)
subject.change_object_data(obj1, data2, is_global=False)
assert obj1.data == data2
assert obj2.data == data1
def test_change_object_data_globally(self):
data1 = bpy.data.meshes.new("Mesh")
data2 = bpy.data.meshes.new("Mesh")
obj1 = bpy.data.objects.new("Object", data1)
obj2 = bpy.data.objects.new("Object", data1)
subject.change_object_data(obj1, data2, is_global=True)
assert obj1.data == data2
assert obj2.data == data2
class TestClearCache(NewFile):
def test_nothing(self):
pass # TODO I don't know how to test this
class TestClearModifiers(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
obj.modifiers.new("IfcOpeningElement", "BOOLEAN")
subject.clear_modifiers(obj)
assert len(obj.modifiers) == 0
class TestClearScale(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
obj.scale[0] = 2
subject.clear_scale(obj)
assert list(obj.scale) == [1, 1, 1]
class TestDeleteData(NewFile):
def test_run(self):
data = bpy.data.meshes.new("Mesh")
subject.delete_data(data)
assert not bpy.data.meshes.get("Mesh")
class TestDoesRepresentationIdExist(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
representation = ifc.createIfcShapeRepresentation()
assert subject.does_representation_id_exist(representation.id()) is True
assert subject.does_representation_id_exist(12345) is False
class TestDuplicateObjectData(NewFile):
def test_run(self):
data = bpy.data.meshes.new("Mesh")
obj = bpy.data.objects.new("Object", data)
new_data = subject.duplicate_object_data(obj)
assert obj.data == data
assert isinstance(new_data, bpy.types.Mesh)
class TestGetObjectData(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(NewFile):
def test_run(self):
material1 = bpy.data.materials.new("Material")
material2 = bpy.data.materials.new("Material")
material3 = bpy.data.materials.new("Material")
material3.BIMStyleProperties.ifc_definition_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 TestGetRepresentationData(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
context = ifc.createIfcGeometricRepresentationContext()
representation = ifc.createIfcShapeRepresentation()
representation.ContextOfItems = context
data = bpy.data.meshes.new("1/2")
assert subject.get_representation_data(representation) == data
class TestGetRepresentationId(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
representation = ifc.createIfcShapeRepresentation()
assert subject.get_representation_id(representation) == representation.id()
class TestGetRepresentationName(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
context = ifc.createIfcGeometricRepresentationContext()
representation = ifc.createIfcShapeRepresentation()
representation.ContextOfItems = context
assert subject.get_representation_name(representation) == f"{context.id()}/{representation.id()}"
class TestGetStyles(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
style = ifc.createIfcSurfaceStyle()
material = bpy.data.materials.new("Material")
tool.Ifc.link(style, material)
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
obj.data.materials.append(material)
assert subject.get_styles(obj) == [style]
class TestGetTextLiteral(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
item = ifc.createIfcTextLiteralWithExtent()
representation = ifc.createIfcShapeRepresentation(Items=[item])
assert subject.get_text_literal(representation) == item
class TestGetCartesianPointCoordinateOffset(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
obj.BIMObjectProperties.cartesian_point_offset = "1,2,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
obj.BIMObjectProperties.cartesian_point_offset = "1,2,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 TestGetElementType(NewFile):
def test_run(self):
bpy.ops.bim.create_project()
ifc = tool.Ifc.get()
element = ifc.createIfcWall()
type = ifc.createIfcWallType()
ifcopenshell.api.run("type.assign_type", ifc, related_objects=[element], relating_type=type)
assert subject.get_element_type(element) == type
class TestGetElementsOfType(NewFile):
def test_run(self):
bpy.ops.bim.create_project()
ifc = tool.Ifc.get()
element = ifc.createIfcWall()
type = ifc.createIfcWallType()
ifcopenshell.api.run("type.assign_type", ifc, related_objects=[element], relating_type=type)
assert subject.get_elements_of_type(type) == (element,)
class TestGetTotalRepresentationItems(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 TestHasDataUsers(NewFile):
def test_run(self):
data = bpy.data.meshes.new("Mesh")
assert subject.has_data_users(data) is False
bpy.data.objects.new("Object", data)
assert subject.has_data_users(data) is True
class TestImportRepresentation(NewFile):
def test_importing_a_normal_shape(self):
ifc = ifcopenshell.open("test/files/basic.ifc")
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
material = bpy.data.materials.new("Material")
material.BIMStyleProperties.ifc_definition_id = 101
element = ifc.by_type("IfcWall")[0]
tool.Ifc.link(element, obj)
representation = element.Representation.Representations[1]
mesh = subject.import_representation(obj, representation)
assert isinstance(mesh, bpy.types.Mesh)
assert len(mesh.polygons) == 12
assert mesh.materials[0] == material
def test_importing_non_body_curves(self):
ifc = ifcopenshell.open("test/files/annotation.ifc")
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
element = ifc.by_type("IfcWall")[0]
tool.Ifc.link(element, obj)
representation = element.Representation.Representations[0]
mesh = subject.import_representation(obj, representation)
assert len(mesh.polygons) == 0
assert len(mesh.edges) == 4
class TestImportRepresentationParameters(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
swept_area = ifc.createIfcCircleProfileDef(Radius=1)
item = ifc.createIfcExtrudedAreaSolid(SweptArea=swept_area, Depth=2)
representation = ifc.createIfcShapeRepresentation(Items=[item])
data = bpy.data.meshes.new("Mesh")
data.BIMMeshProperties.ifc_definition_id = representation.id()
subject.import_representation_parameters(data)
assert data.BIMMeshProperties.ifc_parameters[0].name == "IfcExtrudedAreaSolid/Depth"
assert data.BIMMeshProperties.ifc_parameters[0].step_id == item.id()
assert data.BIMMeshProperties.ifc_parameters[0].index == 3
assert data.BIMMeshProperties.ifc_parameters[1].name == "IfcCircleProfileDef/Radius"
assert data.BIMMeshProperties.ifc_parameters[1].step_id == swept_area.id()
assert data.BIMMeshProperties.ifc_parameters[1].index == 3
class TestIsBodyRepresentation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
context = ifc.createIfcGeometricRepresentationContext()
representation = ifc.createIfcShapeRepresentation()
representation.ContextOfItems = context
context.ContextIdentifier = "Body"
assert subject.is_body_representation(representation) is True
context.ContextIdentifier = "Clearance"
assert subject.is_body_representation(representation) is False
class TestIsBoxRepresentation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
context = ifc.createIfcGeometricRepresentationContext()
representation = ifc.createIfcShapeRepresentation()
representation.ContextOfItems = context
context.ContextIdentifier = "Box"
assert subject.is_box_representation(representation) is True
context.ContextIdentifier = "Clearance"
assert subject.is_box_representation(representation) is False
class TestIsEdited(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
assert subject.is_edited(obj) is False
obj.scale[0] = 2
assert subject.is_edited(obj) is True
obj.scale[0] = 1
assert subject.is_edited(obj) is False
tool.Ifc.edit(obj)
assert subject.is_edited(obj) is True
class TestIsMappedRepresentation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
representation = ifc.createIfcShapeRepresentation()
assert subject.is_mapped_representation(representation) is False
representation.RepresentationType = "MappedRepresentation"
assert subject.is_mapped_representation(representation) is True
class TestIsTypeProduct(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
assert subject.is_type_product(ifc.createIfcWall()) is False
assert subject.is_type_product(ifc.createIfcWallType()) is True
class TestLink(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 TestRecordObjectMaterials(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
style = ifc.createIfcSurfaceStyle()
material = bpy.data.materials.new("Material")
material.BIMStyleProperties.ifc_definition_id = style.id()
obj.data.materials.append(material)
subject.record_object_materials(obj)
assert obj.data.BIMMeshProperties.material_checksum == str([style.id()])
class TestRecordObjectPosition(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.record_object_position(obj)
assert obj.BIMObjectProperties.location_checksum == repr(np.array(obj.matrix_world.translation).tobytes())
assert obj.BIMObjectProperties.rotation_checksum == repr(np.array(obj.matrix_world.to_3x3()).tobytes())
class TestRemoveConnection(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
element1 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
element2 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
rel = ifcopenshell.api.run("geometry.connect_path", ifc, relating_element=element1, related_element=element2)
subject.remove_connection(rel)
assert not tool.Ifc.get().by_type("IfcRelConnectsPathElements")
class TestRenameObject(NewFile):
def test_run(self):
obj = bpy.data.meshes.new("Mesh")
subject.rename_object(obj, "name")
assert obj.name == "name"
class TestReplaceObjectWithEmpty(NewFile):
def validate(
self,
element: ifcopenshell.entity_instance,
obj: bpy.types.Object,
new_obj: bpy.types.Object,
new_data: Union[bpy.types.Mesh, None],
) -> None:
assert not tool.Blender.is_valid_data_block(obj)
assert new_obj.users_collection[0] == bpy.context.scene.collection
assert tool.Ifc.get_entity(new_obj) == element
assert tool.Ifc.get_object(element) == new_obj
assert new_obj.data is new_data
assert new_obj.matrix_world.translation.x == 1
def create_object(
self, name: str, data: Union[bpy.types.Mesh, None], ifc_class: str
) -> tuple[bpy.types.Object, ifcopenshell.entity_instance]:
ifc = tool.Ifc.get()
obj = bpy.data.objects.new(name, data)
obj.matrix_world.translation.x = 1
bpy.context.scene.collection.objects.link(obj)
element = ifc.create_entity(ifc_class)
tool.Ifc.link(element, obj)
return obj, element
def run_test_replace_non_global(
self, from_data: Union[bpy.types.Mesh, None], to_data: Union[bpy.types.Mesh, None]
) -> None:
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj, element = self.create_object("Object", from_data, "IfcWall")
new_obj = subject.recreate_object_with_data(obj, to_data)
if from_data:
assert tool.Blender.is_valid_data_block(from_data)
self.validate(element, obj, new_obj, to_data)
def test_replace_mesh_with_empty(self) -> None:
self.run_test_replace_non_global(bpy.data.meshes.new("Mesh"), None)
def test_replace_empty_with_mesh(self) -> None:
self.run_test_replace_non_global(None, bpy.data.meshes.new("New Mesh"))
def run_test_for_global_argument(
self, from_data: Union[bpy.types.Mesh, None], to_data: Union[bpy.types.Mesh, None]
) -> None:
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
type_obj, element_type = self.create_object("IfcWallType", from_data, "IfcWallType")
obj1, element1 = self.create_object("IfcWall1", from_data, "IfcWall")
obj2, element2 = self.create_object("IfcWall2", from_data, "IfcWall")
ifcopenshell.api.type.assign_type(ifc, related_objects=[element1, element2], relating_type=element_type)
to_data = to_data
new_obj = subject.recreate_object_with_data(type_obj, to_data, is_global=True)
if from_data:
assert tool.Blender.is_valid_data_block(from_data)
self.validate(element_type, type_obj, new_obj, to_data)
self.validate(element1, obj1, bpy.data.objects["IfcWall1"], to_data)
self.validate(element2, obj2, bpy.data.objects["IfcWall2"], to_data)
def test_replace_mesh_with_empty_global(self) -> None:
self.run_test_for_global_argument(bpy.data.meshes.new("Mesh"), None)
def test_replace_empty_with_mesh_global(self) -> None:
self.run_test_for_global_argument(None, bpy.data.meshes.new("New Mesh"))
class TestResolveMappedRepresentation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
mapped_representation = ifc.createIfcShapeRepresentation()
representation = ifc.createIfcShapeRepresentation()
mapped_representation.RepresentationType = "MappedRepresentation"
mapped_representation.Items = [
ifc.createIfcMappedItem(MappingSource=ifc.createIfcRepresentationMap(MappedRepresentation=representation))
]
assert subject.resolve_mapped_representation(mapped_representation) == representation
def test_return_a_representation_if_not_mapped(self):
ifc = ifcopenshell.file()
representation = ifc.createIfcShapeRepresentation()
assert subject.resolve_mapped_representation(representation) == representation
class TestRunGeometryUpdateRepresentation(NewFile):
def test_nothing(self):
pass
class TestRunStyleAddStyle(NewFile):
def test_nothing(self):
pass
class TestSelectConnection(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
element1 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
obj1 = bpy.data.objects.new("Object", None)
bpy.context.scene.collection.objects.link(obj1)
tool.Ifc.link(element1, obj1)
element2 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
obj2 = bpy.data.objects.new("Object", None)
bpy.context.scene.collection.objects.link(obj2)
tool.Ifc.link(element2, obj2)
rel = ifcopenshell.api.run("geometry.connect_path", ifc, relating_element=element1, related_element=element2)
subject.select_connection(rel)
assert obj1 in bpy.context.selected_objects
assert obj2 in bpy.context.selected_objects
class TestShouldForceFacetedBrep(NewFile):
def test_run(self):
result = bpy.context.scene.BIMGeometryProperties.should_force_faceted_brep
assert subject.should_force_faceted_brep() is result
class TestShouldForceTriangulation(NewFile):
def test_run(self):
result = bpy.context.scene.BIMGeometryProperties.should_force_triangulation
assert subject.should_force_triangulation() is result
class TestShouldGenerateUVs(NewFile):
def test_needs_mesh_data(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", None)
assert subject.should_generate_uvs(obj) is False
def test_needs_nodes(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
material = bpy.data.materials.new("Material")
obj.data.materials.append(material)
material.use_nodes = False
assert subject.should_generate_uvs(obj) is False
def test_needs_texture_coordinates_with_a_uv_output(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
material = bpy.data.materials.new("Material")
obj.data.materials.append(material)
material.use_nodes = True
bsdf = tool.Blender.get_material_node(material, "BSDF_PRINCIPLED")
node = material.node_tree.nodes.new(type="ShaderNodeTexImage")
material.node_tree.links.new(bsdf.inputs["Base Color"], node.outputs["Color"])
coords = material.node_tree.nodes.new(type="ShaderNodeTexCoord")
material.node_tree.links.new(node.inputs["Vector"], coords.outputs["UV"])
assert subject.should_generate_uvs(obj) is True
def test_accepts_a_uv_map_node(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
material = bpy.data.materials.new("Material")
obj.data.materials.append(material)
material.use_nodes = True
bsdf = tool.Blender.get_material_node(material, "BSDF_PRINCIPLED")
node = material.node_tree.nodes.new(type="ShaderNodeTexImage")
material.node_tree.links.new(bsdf.inputs["Base Color"], node.outputs["Color"])
coords = material.node_tree.nodes.new(type="ShaderNodeUVMap")
coords.uv_map = "UVMap"
material.node_tree.links.new(node.inputs["Vector"], coords.outputs["UV"])
assert subject.should_generate_uvs(obj) is True
class TestShouldUsePresentationStyleAssignment(NewFile):
def test_run(self):
result = bpy.context.scene.BIMGeometryProperties.should_use_presentation_style_assignment
assert subject.should_use_presentation_style_assignment() is result
class TestGetProfileSetUsage(NewFile):
def test_getting_a_profile_set_usage(self):
ifc = ifcopenshell.file()
element = ifc.createIfcColumn()
assert subject.get_profile_set_usage(element) is None
usage = ifc.createIfcMaterialProfileSetUsage()
ifc.createIfcRelAssociatesMaterial(RelatingMaterial=usage, RelatedObjects=[element])
assert subject.get_profile_set_usage(element) == usage
class TestGetIfcRepresentationClass(NewFile):
def test_detecting_profile_set_representations(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcColumn()
ifc.createIfcRelAssociatesMaterial(
RelatingMaterial=ifc.createIfcMaterialProfileSetUsage(), RelatedObjects=[element]
)
representation = ifc.createIfcShapeRepresentation()
assert (
subject.get_ifc_representation_class(element, representation)
== "IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage"
)
def test_detecting_rectangular_extrusions(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcColumn()
representation = ifc.createIfcShapeRepresentation(
Items=[ifc.createIfcExtrudedAreaSolid(SweptArea=ifc.createIfcRectangleProfileDef())]
)
assert (
subject.get_ifc_representation_class(element, representation)
== "IfcExtrudedAreaSolid/IfcRectangleProfileDef"
)
def test_detecting_circle_extrusions(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcColumn()
representation = ifc.createIfcShapeRepresentation(
Items=[ifc.createIfcExtrudedAreaSolid(SweptArea=ifc.createIfcCircleProfileDef())]
)
assert (
subject.get_ifc_representation_class(element, representation) == "IfcExtrudedAreaSolid/IfcCircleProfileDef"
)
def test_detecting_text_representations(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcAnnotation()
element.ObjectType = "TEXT"
assert subject.get_ifc_representation_class(element, None) == "IfcTextLiteral"
def test_detecting_text_and_geometric_representations(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcAnnotation()
element.ObjectType = "TEXT_LEADER"
assert subject.get_ifc_representation_class(element, None) == "IfcGeometricCurveSet/IfcTextLiteral"
def test_returning_null_for_non_parametric_representations(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
assert subject.get_ifc_representation_class(ifc.createIfcColumn(), ifc.createIfcShapeRepresentation()) is None
class TestRemoveRepresentationItem(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
context = ifc.createIfcGeometricRepresentationContext()
element = ifc.createIfcWall()
items = [ifc.createIfcExtrudedAreaSolid(), ifc.createIfcExtrudedAreaSolid()]
representation = ifc.createIfcShapeRepresentation(Items=items, ContextOfItems=context)
tool.Ifc.run("geometry.assign_representation", product=element, representation=representation)
product_shape = element.Representation
shape_aspect = subject.create_shape_aspect(product_shape, representation, items[:1], None)
shape_aspect_id = shape_aspect.id()
subject.remove_representation_item(items[0])
assert tool.Ifc.get_entity_by_id(shape_aspect_id) is None
assert set(representation.Items) == {items[1]}
class TestCreateShapeAspect(NewFile):
def test_run(self):
self.create_shape_aspect(use_element_type=False)
self.create_shape_aspect(use_element_type=True)
def create_shape_aspect(self, use_element_type):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
context = ifc.createIfcGeometricRepresentationContext()
element = ifc.createIfcWallType() if use_element_type else ifc.createIfcWall()
item = ifc.createIfcExtrudedAreaSolid()
items = [item]
representation = ifc.createIfcShapeRepresentation(Items=items, ContextOfItems=context)
tool.Ifc.run("geometry.assign_representation", product=element, representation=representation)
product_shape = element.RepresentationMaps[0] if use_element_type else element.Representation
subject.create_shape_aspect(product_shape, representation, items, None)
assert len(product_shape.HasShapeAspects) == 1
representation = product_shape.HasShapeAspects[0].ShapeRepresentations[0]
assert set(representation.Items) == set(items)
class TestAddRepresentationItemToShapeAspect(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
context = ifc.createIfcGeometricRepresentationContext()
element = ifc.createIfcWall()
items = [ifc.createIfcExtrudedAreaSolid(), ifc.createIfcExtrudedAreaSolid()]
representation = ifc.createIfcShapeRepresentation(Items=items, ContextOfItems=context)
tool.Ifc.run("geometry.assign_representation", product=element, representation=representation)
product_shape = element.Representation
shape_aspect0 = subject.create_shape_aspect(product_shape, representation, [items[0]], None)
previous_shape_aspect_id = shape_aspect0.id()
shape_aspect1 = subject.create_shape_aspect(product_shape, representation, [items[1]], None)
subject.add_representation_item_to_shape_aspect([items[0]], shape_aspect1)
# previous shape aspect removed as there won't be any items in it
assert tool.Ifc.get_entity_by_id(previous_shape_aspect_id) is None
representation = shape_aspect1.ShapeRepresentations[0]
assert set(representation.Items) == set(items)
class TestRemoveRepresentationItemFromShapeAspect(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
context = ifc.createIfcGeometricRepresentationContext()
element = ifc.createIfcWall()
items = [ifc.createIfcExtrudedAreaSolid(), ifc.createIfcExtrudedAreaSolid()]
representation = ifc.createIfcShapeRepresentation(Items=items, ContextOfItems=context)
tool.Ifc.run("geometry.assign_representation", product=element, representation=representation)
product_shape = element.Representation
shape_aspect0 = subject.create_shape_aspect(product_shape, representation, [items[0]], None)
previous_shape_aspect_id = shape_aspect0.id()
previous_shape_aspect_rep_id = shape_aspect0.ShapeRepresentations[0].id()
shape_aspect1 = subject.create_shape_aspect(product_shape, representation, [items[1]], None)
subject.remove_representation_items_from_shape_aspect([items[0]], shape_aspect0)
# previous shape aspect and representation are removed as there won't be any items in them
assert tool.Ifc.get_entity_by_id(previous_shape_aspect_id) is None
assert tool.Ifc.get_entity_by_id(previous_shape_aspect_rep_id) is None
# items is removed from the representaiton
subject.add_representation_item_to_shape_aspect([items[0]], shape_aspect1)
subject.remove_representation_items_from_shape_aspect([items[1]], shape_aspect1)
representation = shape_aspect1.ShapeRepresentations[0]
assert set(representation.Items) == {items[0]}
class TestApplyItemIdsAsVertexGroups(NewFile):
def test_run(self):
bpy.ops.mesh.primitive_cube_add(size=10, location=(0, 0, 4))
obj = bpy.data.objects["Cube"]
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.quads_convert_to_tris()
bpy.ops.mesh.duplicate_move()
bpy.ops.object.editmode_toggle()
mesh = obj.data
assert isinstance(mesh, bpy.types.Mesh)
assert len(mesh.polygons) == 24
mesh["ios_edges"] = [] # Method requirement.
ios_item_ids = (95,) * 12 + (45,) * 12
mesh["ios_item_ids"] = ios_item_ids
unique = tuple(dict.fromkeys(ios_item_ids))
ios_item_ids_unique = [unique.index(i) for i in ios_item_ids]
tool.Geometry.apply_item_ids_as_vertex_groups(obj)
vertex_group_names = [vg.name for vg in obj.vertex_groups]
assert vertex_group_names == [f"ios_item_id_{i}" for i in unique]
verts = mesh.vertices
for i, polygon in enumerate(mesh.polygons):
for vi in polygon.vertices:
vert = verts[vi]
groups = [g.group for g in vert.groups]
assert groups == [ios_item_ids_unique[i]]
+300
View File
@@ -0,0 +1,300 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import math
import ifcopenshell
import ifcopenshell.api
import blenderbim.core.tool
import blenderbim.tool as tool
from mathutils import Vector
from test.bim.bootstrap import NewFile
from blenderbim.tool.georeference import Georeference as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Georeference)
class TestImportProjectedCRS(NewFile):
def test_importing_nothing_with_no_georeferencing(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
subject.import_projected_crs()
props = bpy.context.scene.BIMGeoreferenceProperties
assert len(props.projected_crs) == 0
def test_importing_projected_crs(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
ifcopenshell.api.run("georeference.add_georeferencing", ifc)
projected_crs = ifc.by_type("IfcProjectedCRS")[0]
projected_crs.Name = "Name"
projected_crs.Description = "Description"
projected_crs.GeodeticDatum = "GeodeticDatum"
projected_crs.VerticalDatum = "VerticalDatum"
projected_crs.MapProjection = "MapProjection"
projected_crs.MapZone = "MapZone"
unit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT")
projected_crs.MapUnit = unit
subject.import_projected_crs()
props = bpy.context.scene.BIMGeoreferenceProperties
assert props.projected_crs.get("Name").string_value == "Name"
assert props.projected_crs.get("Description").string_value == "Description"
assert props.projected_crs.get("GeodeticDatum").string_value == "GeodeticDatum"
assert props.projected_crs.get("VerticalDatum").string_value == "VerticalDatum"
assert props.projected_crs.get("MapProjection").string_value == "MapProjection"
assert props.projected_crs.get("MapZone").string_value == "MapZone"
assert props.projected_crs.get("MapUnit").enum_value == str(unit.id())
def test_run_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
subject.import_projected_crs()
props = bpy.context.scene.BIMGeoreferenceProperties
assert len(props.projected_crs) == 0
class TestImportCoordinateOperation(NewFile):
def test_importing_nothing_with_no_georeferencing(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
subject.import_coordinate_operation()
props = bpy.context.scene.BIMGeoreferenceProperties
assert len(props.coordinate_operation) == 0
def test_importing_coordinate_operation(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Eastings = 1
map_conversion.Northings = 2
map_conversion.OrthogonalHeight = 3
map_conversion.XAxisAbscissa = 4
map_conversion.XAxisOrdinate = 5
map_conversion.Scale = 6
subject.import_coordinate_operation()
props = bpy.context.scene.BIMGeoreferenceProperties
assert props.coordinate_operation.get("Eastings").string_value == "1.0"
assert props.coordinate_operation.get("Northings").string_value == "2.0"
assert props.coordinate_operation.get("OrthogonalHeight").string_value == "3.0"
assert props.x_axis_abscissa == "4.0"
assert props.x_axis_ordinate == "5.0"
assert props.grid_north_angle == "-51.3401917"
assert props.coordinate_operation.get("Scale").string_value == "6.0"
def test_run_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
subject.import_coordinate_operation()
props = bpy.context.scene.BIMGeoreferenceProperties
assert len(props.coordinate_operation) == 0
class TestImportTrueNorth(NewFile):
def test_detecting_no_true_north(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
subject.import_true_north()
props = bpy.context.scene.BIMGeoreferenceProperties
assert props.true_north_abscissa == "0"
assert props.true_north_ordinate == "1"
assert props.true_north_angle == "0"
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
context = ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
context.TrueNorth = ifc.createIfcDirection((1.0, 2.0, 0.0))
subject.import_true_north()
props = bpy.context.scene.BIMGeoreferenceProperties
assert props.true_north_abscissa == "1.0"
assert props.true_north_ordinate == "2.0"
assert props.true_north_angle == "-26.5650512"
class TestExportProjectedCRS(NewFile):
def test_run(self):
TestImportProjectedCRS().test_importing_projected_crs()
assert subject.export_projected_crs() == {
"Name": "Name",
"Description": "Description",
"GeodeticDatum": "GeodeticDatum",
"VerticalDatum": "VerticalDatum",
"MapProjection": "MapProjection",
"MapZone": "MapZone",
"MapUnit": tool.Ifc.get().by_type("IfcNamedUnit")[0],
}
class TestExportCoordinateOperation(NewFile):
def test_run(self):
TestImportCoordinateOperation().test_importing_coordinate_operation()
assert subject.export_coordinate_operation() == {
"Eastings": 1.0,
"Northings": 2.0,
"OrthogonalHeight": 3.0,
"XAxisAbscissa": 4.0,
"XAxisOrdinate": 5.0,
"Scale": 6.0,
}
class TestGetTrueNorthAttributes(NewFile):
def test_run(self):
TestImportTrueNorth().test_run()
assert subject.get_true_north_attributes() == [1.0, 2.0]
class TestEnableEditing(NewFile):
def test_run(self):
bpy.context.scene.BIMGeoreferenceProperties.is_editing = False
subject.enable_editing()
assert bpy.context.scene.BIMGeoreferenceProperties.is_editing is True
class TestDisableEditing(NewFile):
def test_run(self):
bpy.context.scene.BIMGeoreferenceProperties.is_editing = True
subject.disable_editing()
assert bpy.context.scene.BIMGeoreferenceProperties.is_editing is False
class TestSetCoordinates(NewFile):
def test_run(self):
subject.set_coordinates("local", [1.0, 2.0, 3.0])
assert bpy.context.scene.BIMGeoreferenceProperties.local_coordinates == "1.0,2.0,3.0"
subject.set_coordinates("blender", [4.0, 5.0, 6.0])
assert bpy.context.scene.BIMGeoreferenceProperties.blender_coordinates == "4.0,5.0,6.0"
subject.set_coordinates("map", [7.0, 8.0, 9.0])
assert bpy.context.scene.BIMGeoreferenceProperties.map_coordinates == "7.0,8.0,9.0"
class TestGetCoordinates(NewFile):
def test_run(self):
bpy.context.scene.BIMGeoreferenceProperties.local_coordinates = "1.0,2.0,3.0"
assert subject.get_coordinates("local") == [1.0, 2.0, 3.0]
bpy.context.scene.BIMGeoreferenceProperties.blender_coordinates = "4.0,5.0,6.0"
assert subject.get_coordinates("blender") == [4.0, 5.0, 6.0]
bpy.context.scene.BIMGeoreferenceProperties.map_coordinates = "7.0,8.0,9.0"
assert subject.get_coordinates("map") == [7.0, 8.0, 9.0]
class TestGetCursorLocation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
unit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT", prefix="MILLI")
ifcopenshell.api.run("unit.assign_unit", ifc, units=[unit])
bpy.context.scene.cursor.location = (1.0, 2.0, 3.0)
assert subject.get_cursor_location() == [1000.0, 2000.0, 3000.0]
class TestXyz2Enh(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
tool.Ifc.set(ifc)
assert subject.xyz2enh([0.0, 0.0, 0.0]) == (0.0, 0.0, 0.0)
def test_using_the_blender_offset(self):
ifc = ifcopenshell.file()
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
tool.Ifc.set(ifc)
props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = True
props.blender_offset_x = "1.0"
assert subject.xyz2enh([0.0, 0.0, 0.0]) == (1.0, 0.0, 0.0)
def test_using_the_map_conversion(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Eastings = 1.0
assert subject.xyz2enh([0.0, 0.0, 0.0]) == (1.0, 0.0, 0.0)
def test_applying_both_blender_offset_and_map_conversion(self):
props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = True
props.blender_offset_x = "1.0"
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Northings = 1.0
assert subject.xyz2enh([0.0, 0.0, 0.0]) == (1.0, 1.0, 0.0)
class TestEnh2Xyz(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
tool.Ifc.set(ifc)
assert subject.enh2xyz([0.0, 0.0, 0.0]) == (0.0, 0.0, 0.0)
def test_using_the_blender_offset(self):
ifc = ifcopenshell.file()
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
tool.Ifc.set(ifc)
props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = True
props.blender_offset_x = "1.0"
assert subject.enh2xyz([0.0, 0.0, 0.0]) == (-1.0, 0.0, 0.0)
def test_using_the_map_conversion(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Eastings = 1.0
assert subject.enh2xyz([0.0, 0.0, 0.0]) == (-1.0, 0.0, 0.0)
def test_applying_both_blender_offset_and_map_conversion(self):
props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = True
props.blender_offset_x = "1.0"
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Northings = 1.0
assert subject.enh2xyz([0.0, 0.0, 0.0]) == (-1.0, -1.0, 0.0)
+223
View File
@@ -0,0 +1,223 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. 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
import pytest
from blenderbim.tool.ifc import Ifc as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Ifc)
class TestSet(test.bim.bootstrap.NewFile):
def test_setting_an_ifc_data(self):
ifc = ifcopenshell.file()
subject.set(ifc)
assert subject.get() == ifc
class TestGet(test.bim.bootstrap.NewFile):
def test_getting_an_ifc_dataset_from_a_ifc_spf_filepath(self):
assert subject.get() is None
bpy.context.scene.BIMProperties.ifc_file = "test/files/basic.ifc"
result = subject.get()
assert isinstance(result, ifcopenshell.file)
def test_getting_the_active_ifc_dataset_regardless_of_ifc_path(self):
bpy.context.scene.BIMProperties.ifc_file = "test/files/basic.ifc"
ifc = ifcopenshell.file()
subject.set(ifc)
assert subject.get() == ifc
class TestRun(test.bim.bootstrap.NewFile):
def test_running_a_command_on_the_active_ifc_dataset(self):
ifc = ifcopenshell.file()
subject.set(ifc)
wall = subject.run("root.create_entity", ifc_class="IfcWall")
assert subject.get().by_type("IfcWall")[0] == wall
class TestGetSchema(test.bim.bootstrap.NewFile):
def test_getting_the_schema_version_identifier(self):
ifc = ifcopenshell.file(schema="IFC4")
subject.set(ifc)
assert subject.get_schema() == "IFC4"
class TestIsMoved(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
subject.set(ifc)
obj = bpy.data.objects.new("Object", None)
element = ifc.create_entity("IfcWall")
subject.link(element, obj)
assert subject.is_moved(obj) is True
tool.Geometry.record_object_position(obj)
assert subject.is_moved(obj) is False
obj.matrix_world[0][2] += 1
assert subject.is_moved(obj) is True
def test_that_a_type_or_project_never_moves_but_a_grid_axis_does(self):
ifc = ifcopenshell.file()
subject.set(ifc)
obj = bpy.data.objects.new("Object", None)
element = ifc.create_entity("IfcWallType")
subject.link(element, obj)
assert subject.is_moved(obj) is False
element = ifc.create_entity("IfcProject")
subject.link(element, obj)
assert subject.is_moved(obj) is False
element = ifc.create_entity("IfcGridAxis")
subject.link(element, obj)
assert subject.is_moved(obj) is True
class TestGetEntity(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
subject.set(ifc)
obj = bpy.data.objects.new("Object", None)
element = ifc.create_entity("IfcWall")
subject.link(element, obj)
assert subject.get_entity(obj) == element
def test_attempting_to_get_an_unlinked_object(self):
obj = bpy.data.objects.new("Object", None)
assert subject.get_entity(obj) is None
def test_attempting_without_a_file(self):
obj = bpy.data.objects.new("Object", None)
obj.BIMObjectProperties.ifc_definition_id = 1
assert subject.get_entity(obj) is None
def test_attempting_to_get_an_invalidly_linked_object(self):
ifc = ifcopenshell.file()
subject.set(ifc)
obj = bpy.data.objects.new("Object", None)
obj.BIMObjectProperties.ifc_definition_id = 1
assert subject.get_entity(obj) is None
class TestGetObject(test.bim.bootstrap.NewFile):
def test_get_object(self):
ifc = ifcopenshell.file()
subject.set(ifc)
element = ifc.create_entity("IfcWall")
obj = bpy.data.objects.new("Object", None)
subject.link(element, obj)
assert subject.get_object(element) == obj
def test_get_material(self):
ifc = ifcopenshell.file()
subject.set(ifc)
element = ifc.create_entity("IfcSurfaceStyle")
obj = bpy.data.materials.new("Style")
subject.link(element, obj)
assert subject.get_object(element) == obj
class TestLink(test.bim.bootstrap.NewFile):
def test_link_an_object(self):
ifc = ifcopenshell.file()
subject.set(ifc)
element = ifc.create_entity("IfcWall")
obj = bpy.data.objects.new("Object", None)
subject.link(element, obj)
assert subject.get_entity(obj) == element
assert subject.get_object(element) == obj
def test_link_a_style(self):
ifc = ifcopenshell.file()
subject.set(ifc)
element = ifc.create_entity("IfcSurfaceStyle")
obj = bpy.data.materials.new("Material")
subject.link(element, obj)
assert subject.get_entity(obj) == element
assert subject.get_object(element) == obj
def test_link_not_a_style_to_blender_material(self):
ifc = ifcopenshell.file()
subject.set(ifc)
element = ifc.create_entity("IfcMaterial")
obj = bpy.data.materials.new("Material")
with pytest.raises(AttributeError):
subject.link(element, obj)
def test_link_a_mesh(self):
ifc = ifcopenshell.file()
subject.set(ifc)
element = ifc.create_entity("IfcShapeRepresentation")
obj = bpy.data.meshes.new("Material")
subject.link(element, obj)
assert obj.BIMMeshProperties.ifc_definition_id == element.id()
class TestUnlink(test.bim.bootstrap.NewFile):
def test_unlink_an_object(self):
ifc = ifcopenshell.file()
subject.set(ifc)
element = ifc.create_entity("IfcWall")
obj = bpy.data.objects.new("Object", None)
subject.link(element, obj)
subject.unlink(element)
assert subject.get_entity(obj) is None
assert subject.get_object(element) is None
def test_unlink_a_style(self):
ifc = ifcopenshell.file()
subject.set(ifc)
element = ifc.create_entity("IfcSurfaceStyle")
obj = bpy.data.materials.new("Material")
subject.link(element, obj)
subject.unlink(element)
assert subject.get_entity(obj) is None
assert subject.get_object(element) is None
def test_unlinking_using_an_object(self):
ifc = ifcopenshell.file()
subject.set(ifc)
element = ifc.create_entity("IfcWall")
obj = bpy.data.objects.new("Object", None)
subject.link(element, obj)
subject.unlink(obj=obj)
assert subject.get_entity(obj) is None
assert subject.get_object(element) is obj
def test_unlinking_using_both_arguments(self):
ifc = ifcopenshell.file()
subject.set(ifc)
element = ifc.create_entity("IfcWall")
obj = bpy.data.objects.new("Object", None)
subject.link(element, obj)
with pytest.raises(TypeError):
subject.unlink(element=element, obj=obj)
assert subject.get_entity(obj) == element
assert subject.get_object(element) == obj
+134
View File
@@ -0,0 +1,134 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. 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.library import Library as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Library)
class TestClearEditingMode(NewFile):
def test_run(self):
props = bpy.context.scene.BIMLibraryProperties
props.editing_mode = "foo"
subject.clear_editing_mode()
assert props.editing_mode == ""
class TestExportLibraryAttributes(NewFile):
def test_run(self):
TestImportLibraryAttributes().test_run()
assert subject.export_library_attributes() == {
"Name": "Name",
"Version": "Version",
"VersionDate": "VersionDate",
"Location": "Location",
"Description": "Description",
}
class TestExportReferenceAttributes(NewFile):
def test_run(self):
TestImportReferenceAttributes().test_run()
assert subject.export_reference_attributes() == {
"Location": "Location",
"Identification": "Identification",
"Name": "Name",
"Description": "Description",
"Language": "Language",
}
class TestGetActiveLibrary(NewFile):
def test_run(self):
TestSetActiveLibrary().test_run()
assert subject.get_active_library().is_a("IfcLibraryInformation")
class TestGetActiveReference(NewFile):
def test_run(self):
TestSetActiveReference().test_run()
assert subject.get_active_reference().is_a("IfcLibraryReference")
class TestImportLibraryAttributes(NewFile):
def test_run(self):
tool.Ifc.set(ifc := ifcopenshell.file())
library = ifc.createIfcLibraryInformation("Name", "Version", None, "VersionDate", "Location", "Description")
subject.import_library_attributes(library)
props = bpy.context.scene.BIMLibraryProperties
assert props.library_attributes.get("Name").string_value == "Name"
assert props.library_attributes.get("Version").string_value == "Version"
assert props.library_attributes.get("VersionDate").string_value == "VersionDate"
assert props.library_attributes.get("Location").string_value == "Location"
assert props.library_attributes.get("Description").string_value == "Description"
class TestImportReferenceAttributes(NewFile):
def test_run(self):
tool.Ifc.set(ifc := ifcopenshell.file())
reference = ifc.createIfcLibraryReference("Location", "Identification", "Name", "Description", "Language")
subject.import_reference_attributes(reference)
props = bpy.context.scene.BIMLibraryProperties
assert props.reference_attributes.get("Location").string_value == "Location"
assert props.reference_attributes.get("Identification").string_value == "Identification"
assert props.reference_attributes.get("Name").string_value == "Name"
assert props.reference_attributes.get("Description").string_value == "Description"
assert props.reference_attributes.get("Language").string_value == "Language"
class TestImportReferences(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
library = ifc.createIfcLibraryInformation()
reference = ifc.createIfcLibraryReference(Name="Reference", ReferencedLibrary=library)
subject.import_references(library)
props = bpy.context.scene.BIMLibraryProperties
assert props.references[0].ifc_definition_id == reference.id()
assert props.references[0].name == "Reference"
class TestSetActiveLibrary(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
library = ifc.createIfcLibraryInformation()
subject.set_active_library(library)
assert bpy.context.scene.BIMLibraryProperties.active_library_id == library.id()
class TestSetActiveReference(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
reference = ifc.createIfcLibraryReference()
subject.set_active_reference(reference)
assert bpy.context.scene.BIMLibraryProperties.active_reference_id == reference.id()
class TestSetEditingMode(NewFile):
def test_run(self):
subject.set_editing_mode("FOO")
assert bpy.context.scene.BIMLibraryProperties.editing_mode == "FOO"
+501
View File
@@ -0,0 +1,501 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import bmesh
import ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.loader import Loader as subject
import numpy as np
from ifcopenshell.util.shape_builder import ShapeBuilder, V
from mathutils import Vector
from pathlib import Path
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Loader)
class TestCreatingStyles(NewFile):
def test_create_surface_style_with_textures_from_data(self):
# this case occurs when we edit shader properties without saving them to IFC
bpy.ops.bim.create_project()
bpy.ops.mesh.primitive_cube_add(size=10, location=(0, 0, 4))
obj = bpy.data.objects["Cube"]
bpy.ops.bim.assign_class(ifc_class="IfcActuator", predefined_type="ELECTRICACTUATOR", userdefined_type="")
material = bpy.data.materials.new(name="Material")
obj.data.materials.append(None) # new slot
obj.material_slots[0].material = material
bpy.ops.bim.add_style()
style_data = {
"ReflectanceMethod": "NOTDEFINED",
"DiffuseColour": ("IfcColourRgb", (0.5, 0.5, 0.5)),
"SurfaceColour": (0.3, 0.3, 0.3),
"Transparency": 0.3,
"SpecularHighlight": 0.4,
"SpecularColour": ("IfcNormalisedRatioMeasure", 0.03),
}
texture_data = [
{
"Mode": "DIFFUSE",
"URLReference": "blenderbim/test/files/image.jpg",
"type": "IfcImageTexture",
"uv_mode": "Generated",
},
]
subject.create_surface_style_rendering(material, style_data)
subject.create_surface_style_with_textures(material, style_data, texture_data)
used_node_types = set([n.type for n in material.node_tree.nodes[:]])
assert used_node_types == set((["OUTPUT_MATERIAL", "BSDF_PRINCIPLED", "TEX_IMAGE", "TEX_COORD"]))
bsdf = tool.Blender.get_material_node(material, "BSDF_PRINCIPLED")
alpha = 1 - style_data["Transparency"]
diffuse_color = style_data["SurfaceColour"] + (alpha,)
assert np.allclose(material.diffuse_color[:], diffuse_color)
base_color = style_data["DiffuseColour"][1] + (1.0,)
assert np.allclose(bsdf.inputs["Base Color"].default_value[:], base_color)
assert np.isclose(bsdf.inputs["Alpha"].default_value, alpha)
assert np.isclose(bsdf.inputs["Roughness"].default_value, style_data["SpecularHighlight"])
assert np.isclose(bsdf.inputs["Metallic"].default_value, style_data["SpecularColour"][1])
image_node = tool.Blender.get_material_node(material, "TEX_IMAGE")
assert image_node.outputs["Color"].links[0].to_socket.name == "Base Color"
assert image_node.inputs["Vector"].links[0].from_socket.name == "Generated"
original_path = Path(tool.Ifc.get_path()).parent / Path(texture_data[0]["URLReference"])
loaded_filepath = Path(image_node.image.filepath)
assert original_path == loaded_filepath
def test_create_surface_style_with_textures_from_ifc(self):
# this case occurs when we edit shader properties without saving them to IFC
bpy.ops.bim.create_project()
style = tool.Ifc.run("style.add_style", name="test")
material = bpy.data.materials.new(style.Name)
tool.Ifc.link(style, material)
get_color = lambda value: {color: value for color in ("Red", "Green", "Blue")}
rendering_attributes = {
"ReflectanceMethod": "NOTDEFINED",
"DiffuseColour": get_color(0.5),
"SurfaceColour": get_color(0.3),
"Transparency": 0.3,
"SpecularHighlight": {"IfcSpecularRoughness": 0.4},
"SpecularColour": 0.03,
}
rendering_style = tool.Ifc.run(
"style.add_surface_style",
style=style,
ifc_class="IfcSurfaceStyleRendering",
attributes=rendering_attributes,
)
textures = [
{
"URLReference": "blenderbim/test/files/image.jpg",
"Mode": "DIFFUSE",
"RepeatS": True,
"RepeatT": True,
"uv_mode": "Generated",
}
]
textures = tool.Ifc.run("style.add_surface_textures", textures=textures, uv_maps=[])
texture_style = tool.Ifc.run(
"style.add_surface_style",
style=style,
ifc_class="IfcSurfaceStyleWithTextures",
attributes={"Textures": textures},
)
subject.create_surface_style_rendering(material, rendering_style)
subject.create_surface_style_with_textures(material, rendering_style, texture_style)
used_node_types = set([n.type for n in material.node_tree.nodes[:]])
assert used_node_types == set((["OUTPUT_MATERIAL", "BSDF_PRINCIPLED", "TEX_IMAGE", "TEX_COORD"]))
color_to_tuple = lambda x: (x.Red, x.Green, x.Blue)
bsdf = tool.Blender.get_material_node(material, "BSDF_PRINCIPLED")
alpha = 1 - rendering_style.Transparency
diffuse_color = color_to_tuple(rendering_style.SurfaceColour) + (alpha,)
assert np.allclose(material.diffuse_color[:], diffuse_color)
base_color = color_to_tuple(rendering_style.DiffuseColour) + (1.0,)
assert np.allclose(bsdf.inputs["Base Color"].default_value[:], base_color)
assert np.isclose(bsdf.inputs["Alpha"].default_value, alpha)
assert np.isclose(bsdf.inputs["Roughness"].default_value, rendering_style.SpecularHighlight.wrappedValue)
assert np.isclose(bsdf.inputs["Metallic"].default_value, rendering_style.SpecularColour.wrappedValue)
image_node = tool.Blender.get_material_node(material, "TEX_IMAGE")
assert image_node.outputs["Color"].links[0].to_socket.name == "Base Color"
assert image_node.inputs["Vector"].links[0].from_socket.name == "Generated"
original_path = Path(tool.Ifc.get_path()).parent / Path(textures[0].URLReference)
loaded_filepath = Path(image_node.image.filepath)
assert original_path == loaded_filepath
def test_create_surface_style_with_textures_from_ifc_blob_image(self):
# this case occurs when we edit shader properties without saving them to IFC
bpy.ops.bim.create_project()
ifc_file = tool.Ifc.get()
style = tool.Ifc.run("style.add_style", name="test")
material = bpy.data.materials.new(style.Name)
tool.Ifc.link(style, material)
get_color = lambda value: {color: value for color in ("Red", "Green", "Blue")}
rendering_attributes = {
"ReflectanceMethod": "NOTDEFINED",
"DiffuseColour": get_color(0.5),
"SurfaceColour": get_color(0.3),
"Transparency": 0.3,
"SpecularHighlight": {"IfcSpecularRoughness": 0.4},
"SpecularColour": 0.03,
}
rendering_style = tool.Ifc.run(
"style.add_surface_style",
style=style,
ifc_class="IfcSurfaceStyleRendering",
attributes=rendering_attributes,
)
def get_png_raster_code():
import base64
import zlib
import struct
# https://blender.stackexchange.com/questions/62072/does-blender-have-a-method-to-a-get-png-formatted-bytearray-for-an-image-via-pyt
width = 2
height = 2
# 2x2 image:
# R G
# B W
# fmt: off
pixels = (
1,0,0,1,
0,1,0,1,
0,0,1,1,
1,1,1,1,
)
# fmt: on
buf = bytearray([int(p * 255) for p in pixels])
# add null bytes at the start
width_byte_4 = width * 4
raw_data = b"".join(
b"\x00" + buf[span : span + width_byte_4] for span in range(0, height * width_byte_4, width_byte_4)
)
def png_pack(png_tag, data):
header = png_tag + data
chunk = struct.pack("!I", len(data))
chunk += header
chunk += struct.pack("!I", 0xFFFFFFFF & zlib.crc32(header))
return chunk
png_bytes = b"".join(
[
b"\x89PNG\r\n\x1a\n",
png_pack(b"IHDR", struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
png_pack(b"IDAT", zlib.compress(raw_data, 9)),
png_pack(b"IEND", b""),
]
)
raster_code = "".join(["{:08b}".format(x) for x in png_bytes])
return raster_code
texture_data = {
"RasterCode": get_png_raster_code(),
"RasterFormat": "PNG",
"Mode": "DIFFUSE",
"RepeatS": True,
"RepeatT": True,
}
# setup texture manually as `style.add_surface_textures` doesn't support non-IfcImageTexture textures
textures = [ifc_file.create_entity("IfcBlobTexture", **texture_data)]
# setup UV
ifc_file.create_entity("IfcTextureCoordinateGenerator", Maps=textures, Mode="COORD")
texture_style = tool.Ifc.run(
"style.add_surface_style",
style=style,
ifc_class="IfcSurfaceStyleWithTextures",
attributes={"Textures": textures},
)
subject.create_surface_style_rendering(material, rendering_style)
subject.create_surface_style_with_textures(material, rendering_style, texture_style)
used_node_types = set([n.type for n in material.node_tree.nodes[:]])
assert used_node_types == set((["OUTPUT_MATERIAL", "BSDF_PRINCIPLED", "TEX_IMAGE", "TEX_COORD"]))
image_node = tool.Blender.get_material_node(material, "TEX_IMAGE")
assert image_node.outputs["Color"].links[0].to_socket.name == "Base Color"
assert image_node.inputs["Vector"].links[0].from_socket.name == "Generated"
assert image_node.image.filepath == ""
# fmt: off
# blender has reversed pixels rows order
assert image_node.image.pixels[:] == (0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0)
# fmt: on
def test_create_surface_style_with_textures_from_ifc_pixel_image(self):
# fmt: off
# blender has reversed pixels rows order
# 2x2 image:
# R G
# B W
ifc_pixel_data = {
1: (
0.7,
1.0,
0.5,
0.6,
),
2: (
0.7, 0.5,
1.0, 0.5,
0.5, 0.5,
0.6, 0.5
),
3: (
0, 0, 0.7,
1, 1, 1,
0.5, 0, 0,
0, 0.6, 0,
),
4: (
0, 0, 0.7, 0.5,
1, 1, 1, 0.5,
0.5, 0, 0, 0.5,
0, 0.6, 0, 0.5,
)
}
expected_pixel_data = {
1: (
0.7, 0.7, 0.7, 1.0,
1.0, 1.0, 1.0, 1.0,
0.5, 0.5, 0.5, 1.0,
0.6, 0.6, 0.6, 1.0,
),
2: (
0.7, 0.7, 0.7, 0.5,
1.0, 1.0, 1.0, 0.5,
0.5, 0.5, 0.5, 0.5,
0.6, 0.6, 0.6, 0.5,
),
3: (
0, 0, 0.7, 1.0,
1, 1, 1, 1.0,
0.5, 0, 0, 1.0,
0, 0.6, 0, 1.0,
),
4: (
0, 0, 0.7, 0.5,
1, 1, 1, 0.5,
0.5, 0, 0, 0.5,
0, 0.6, 0, 0.5,
)
}
# fmt: on
for i in range(1, 5):
self.run_test_create_surface_style_with_textures_from_ifc_pixel_image(
i, ifc_pixel_data[i], expected_pixel_data[i]
)
def run_test_create_surface_style_with_textures_from_ifc_pixel_image(
self, n_components, ifc_pixel_data, expected_pixel_data
):
# this case occurs when we edit shader properties without saving them to IFC
bpy.ops.bim.create_project()
ifc_file = tool.Ifc.get()
style = tool.Ifc.run("style.add_style", name="test")
material = bpy.data.materials.new(style.Name)
tool.Ifc.link(style, material)
get_color = lambda value: {color: value for color in ("Red", "Green", "Blue")}
rendering_attributes = {
"ReflectanceMethod": "NOTDEFINED",
"DiffuseColour": get_color(0.5),
"SurfaceColour": get_color(0.3),
"Transparency": 0.3,
"SpecularHighlight": {"IfcSpecularRoughness": 0.4},
"SpecularColour": 0.03,
}
rendering_style = tool.Ifc.run(
"style.add_surface_style",
style=style,
ifc_class="IfcSurfaceStyleRendering",
attributes=rendering_attributes,
)
def get_pixels_binary():
# just 4 pixels - red, green, blue, white
# IfcPixelTexture as Blender has reversed order of pixels rows: (0,0) - bottom left of the image
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcPixelTexture.htm
# fmt: off
pixels = np.array(ifc_pixel_data) * 255
pixels = pixels.reshape((4, n_components))
return ["".join(["{:08b}".format(int(i)) for i in pixel]) for pixel in pixels]
texture_data = {
"Pixel": get_pixels_binary(),
"ColourComponents": n_components,
"Width": 2,
"Height": 2,
"Mode": "DIFFUSE",
"RepeatS": True,
"RepeatT": True,
}
# setup texture manually as `style.add_surface_textures` doesn't support non-IfcImageTexture textures
textures = [ifc_file.create_entity("IfcPixelTexture", **texture_data)]
# setup UV
ifc_file.create_entity("IfcTextureCoordinateGenerator", Maps=textures, Mode="COORD")
texture_style = tool.Ifc.run(
"style.add_surface_style",
style=style,
ifc_class="IfcSurfaceStyleWithTextures",
attributes={"Textures": textures},
)
subject.create_surface_style_rendering(material, rendering_style)
subject.create_surface_style_with_textures(material, rendering_style, texture_style)
used_node_types = set([n.type for n in material.node_tree.nodes[:]])
assert used_node_types == set((["OUTPUT_MATERIAL", "BSDF_PRINCIPLED", "TEX_IMAGE", "TEX_COORD"]))
image_node = tool.Blender.get_material_node(material, "TEX_IMAGE")
assert image_node.outputs["Color"].links[0].to_socket.name == "Base Color"
assert image_node.inputs["Vector"].links[0].from_socket.name == "Generated"
assert image_node.image.filepath == ""
assert np.allclose(
image_node.image.pixels[:], expected_pixel_data, atol=0.01
), f"Failed to match pixels for {n_components}.\nBlender pixel_data: {image_node.image.pixels[:]}.\nExpected data: {expected_pixel_data}"
class TestLoadingIndexedTextureMap(NewFile):
def test_run(self):
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
bpy.ops.bim.create_project()
# TODO: replace with loading geometry from IFC
# create a cube without uv and triangulate it
mesh = bpy.data.meshes.new("Cube")
bm = tool.Blender.get_bmesh_for_mesh(mesh, clean=True)
bmesh.ops.create_cube(bm, size=2.0, calc_uvs=False)
bmesh.ops.triangulate(bm, faces=bm.faces[:])
tool.Blender.apply_bmesh(mesh, bm)
ifc_file = tool.Ifc.get()
builder = ShapeBuilder(ifc_file)
# tesselated cube
points = (
V(-1000, -1000, -1000),
V(-1000, -1000, 1000),
V(-1000, 1000, -1000),
V(-1000, 1000, 1000),
V(1000, -1000, -1000),
V(1000, -1000, 1000),
V(1000, 1000, -1000),
V(1000, 1000, 1000),
)
faces = (
(1, 2, 0),
(3, 6, 2),
(7, 4, 6),
(5, 0, 4),
(6, 0, 2),
(3, 5, 7),
(1, 3, 2),
(3, 7, 6),
(7, 5, 4),
(5, 1, 0),
(6, 4, 0),
(3, 1, 5),
)
face_set = builder.polygonal_face_set(points, faces)
uv_indices = (
(1, 2, 3),
(4, 5, 6),
(7, 8, 9),
(10, 11, 12),
(13, 14, 15),
(16, 17, 18),
(19, 20, 21),
(22, 23, 24),
(25, 26, 27),
(28, 29, 30),
(31, 32, 33),
(34, 35, 36),
)
uv_verts = (
(0.625, 0.0),
(0.375, 0.25),
(0.375, 0.0),
(0.625, 0.25),
(0.375, 0.5),
(0.375, 0.25),
(0.625, 0.5),
(0.375, 0.75),
(0.375, 0.5),
(0.625, 0.75),
(0.375, 1.0),
(0.375, 0.75),
(0.375, 0.5),
(0.125, 0.75),
(0.125, 0.5),
(0.875, 0.5),
(0.625, 0.75),
(0.625, 0.5),
(0.625, 0.0),
(0.625, 0.25),
(0.375, 0.25),
(0.625, 0.25),
(0.625, 0.5),
(0.375, 0.5),
(0.625, 0.5),
(0.625, 0.75),
(0.375, 0.75),
(0.625, 0.75),
(0.625, 1.0),
(0.375, 1.0),
(0.375, 0.5),
(0.375, 0.75),
(0.125, 0.75),
(0.875, 0.5),
(0.875, 0.75),
(0.625, 0.75),
)
uv_verts_list = ifc_file.createIfcTextureVertexList()
uv_verts_list.TexCoordsList = uv_verts
texture_coord = ifc_file.createIfcIndexedTriangleTextureMap()
texture_coord.MappedTo = face_set
texture_coord.TexCoordIndex = uv_indices
texture_coord.TexCoords = uv_verts_list
subject.load_indexed_texture_map(texture_coord, mesh)
uv_layer = mesh.uv_layers.active
assert uv_layer is not None
for ifc_uv, blender_uv in zip(uv_verts, uv_layer.uv, strict=True):
assert tool.Cad.are_vectors_equal(Vector(ifc_uv), blender_uv.vector)
+152
View File
@@ -0,0 +1,152 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.api
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.material import Material as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Material)
class TestDisableEditingMaterials(NewFile):
def test_run(self):
bpy.context.scene.BIMMaterialProperties.is_editing = True
subject.disable_editing_materials()
assert bpy.context.scene.BIMMaterialProperties.is_editing is False
class TestEnableEditingMaterials(NewFile):
def test_run(self):
bpy.context.scene.BIMMaterialProperties.is_editing = False
subject.enable_editing_materials()
assert bpy.context.scene.BIMMaterialProperties.is_editing is True
class TestGetActiveMaterialType(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
bpy.context.scene.BIMMaterialProperties.material_type = "IfcMaterial"
assert subject.get_active_material_type() == "IfcMaterial"
bpy.context.scene.BIMMaterialProperties.material_type = "IfcMaterialLayerSet"
assert subject.get_active_material_type() == "IfcMaterialLayerSet"
class TestGetElementsByMaterial(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
material = ifcopenshell.api.run("material.add_material", ifc)
ifcopenshell.api.run("material.assign_material", ifc, products=[element], material=material)
assert subject.get_elements_by_material(material) == {element}
class TestImportMaterialDefinitions(NewFile):
def test_import_materials_grouped_by_categories(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterial(Name="Name", Category="Category")
subject.import_material_definitions("IfcMaterial")
props = bpy.context.scene.BIMMaterialProperties
assert props.materials[0].ifc_definition_id == 0
assert props.materials[0].name == "Category"
assert props.materials[0].is_category is True
assert props.materials[0].is_expanded is False
assert len(props.materials) == 1
def test_importing_expanded_material_categories(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterial(Name="Name", Category="Category")
subject.import_material_definitions("IfcMaterial")
props = bpy.context.scene.BIMMaterialProperties
props.materials[0].is_expanded = True
subject.import_material_definitions("IfcMaterial")
assert len(props.materials) == 2
assert props.materials[1].ifc_definition_id == material.id()
assert props.materials[1].name == "Name"
assert props.materials[1].total_elements == 0
def test_import_material_layer_sets(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterialLayerSet(LayerSetName="Name")
subject.import_material_definitions("IfcMaterialLayerSet")
props = bpy.context.scene.BIMMaterialProperties
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Name"
assert props.materials[0].total_elements == 0
def test_import_material_profile_sets(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterialProfileSet(Name="Name")
subject.import_material_definitions("IfcMaterialProfileSet")
props = bpy.context.scene.BIMMaterialProperties
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Name"
assert props.materials[0].total_elements == 0
def test_import_material_constituent_sets(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterialConstituentSet(Name="Name")
subject.import_material_definitions("IfcMaterialConstituentSet")
props = bpy.context.scene.BIMMaterialProperties
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Name"
assert props.materials[0].total_elements == 0
def test_import_material_lists(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterialList()
subject.import_material_definitions("IfcMaterialList")
props = bpy.context.scene.BIMMaterialProperties
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Unnamed"
assert props.materials[0].total_elements == 0
class TestIsEditingMaterials(NewFile):
def test_run(self):
bpy.context.scene.BIMMaterialProperties.is_editing = False
assert subject.is_editing_materials() is False
bpy.context.scene.BIMMaterialProperties.is_editing = True
assert subject.is_editing_materials() is True
class TestIsMaterialUsedInSets(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material_set = ifc.createIfcMaterialLayerSet()
material_set_item = ifc.createIfcMaterialLayer()
material = ifc.createIfcMaterial()
assert subject.is_material_used_in_sets(material) is False
material_set.MaterialLayers = [material_set_item]
material_set_item.Material = material
assert subject.is_material_used_in_sets(material) is True
+187
View File
@@ -0,0 +1,187 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. 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 blenderbim.tool.misc import Misc as subject
from blenderbim.bim.ifc import IfcStore
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Misc)
class TestGetObjectStorey(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", None)
wall = ifc.createIfcWall()
tool.Ifc.link(wall, obj)
storey = ifc.createIfcBuildingStorey()
ifcopenshell.api.run("spatial.assign_container", ifc, products=[wall], relating_structure=storey)
assert subject.get_object_storey(obj) == storey
def test_only_returning_a_building_storey(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", None)
wall = ifc.createIfcWall()
tool.Ifc.link(wall, obj)
building = ifc.createIfcBuilding()
ifcopenshell.api.run("spatial.assign_container", ifc, products=[wall], relating_structure=building)
assert subject.get_object_storey(obj) is None
def test_returning_nothing_if_uncontained(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", None)
wall = ifc.createIfcWall()
tool.Ifc.link(wall, obj)
assert subject.get_object_storey(obj) is None
class TestGetStoreyElevation(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
ifc.createIfcProject()
unit = ifcopenshell.api.run("unit.add_si_unit", ifc, prefix="MILLI")
ifcopenshell.api.run("unit.assign_unit", ifc, units=[unit])
storey = ifc.createIfcBuildingStorey()
storey.Elevation = 3000
tool.Ifc.set(ifc)
assert subject.get_storey_elevation_in_si(storey) == 3.0
class TestGetStoreyHeight(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
ifc.createIfcProject()
unit = ifcopenshell.api.run("unit.add_si_unit", ifc, prefix="MILLI")
ifcopenshell.api.run("unit.assign_unit", ifc, units=[unit])
tool.Ifc.set(ifc)
building = ifc.createIfcBuilding()
storey = ifc.createIfcBuildingStorey()
storey.Elevation = 3000
storey2 = ifc.createIfcBuildingStorey()
storey2.Elevation = 5000
ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey], relating_object=building)
ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey2], relating_object=building)
assert subject.get_storey_height_in_si(storey, 1) == 2.0
def test_getting_a_double_storey_height(self):
ifc = ifcopenshell.file()
ifc.createIfcProject()
unit = ifcopenshell.api.run("unit.add_si_unit", ifc, prefix="MILLI")
ifcopenshell.api.run("unit.assign_unit", ifc, units=[unit])
tool.Ifc.set(ifc)
building = ifc.createIfcBuilding()
storey = ifc.createIfcBuildingStorey()
storey.Elevation = 3000
storey2 = ifc.createIfcBuildingStorey()
storey2.Elevation = 5000
storey3 = ifc.createIfcBuildingStorey()
storey3.Elevation = 9000
ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey], relating_object=building)
ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey2], relating_object=building)
ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey3], relating_object=building)
assert subject.get_storey_height_in_si(storey, 2) == 6.0
def test_only_considering_storeys_in_the_same_building(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
building = ifc.createIfcBuilding()
storey = ifc.createIfcBuildingStorey()
storey.Elevation = 3000
storey2 = ifc.createIfcBuildingStorey()
storey2.Elevation = 5000
ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey], relating_object=building)
assert subject.get_storey_height_in_si(storey, 1) is None
def test_returning_none_if_the_storey_height_is_undefined(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
building = ifc.createIfcBuilding()
storey = ifc.createIfcBuildingStorey()
ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey], relating_object=building)
assert subject.get_storey_height_in_si(storey, 1) is None
class TestSetObjectOriginToBottom(test.bim.bootstrap.NewFile):
def test_run(self):
bpy.ops.mesh.primitive_cube_add()
obj = bpy.data.objects.get("Cube")
subject.set_object_origin_to_bottom(obj)
assert list(obj.matrix_world.translation) == [0.0, 0.0, -1.0]
def test_moving_the_origin_of_a_rotated_object(self):
bpy.ops.mesh.primitive_cube_add()
obj = bpy.data.objects.get("Cube")
obj.rotation_euler[1] = math.pi / 2
bpy.context.view_layer.update()
subject.set_object_origin_to_bottom(obj)
assert list(obj.matrix_world.translation) == [0.0, 0.0, -1.0]
class TestMoveObjectToElevation(test.bim.bootstrap.NewFile):
def test_run(self):
bpy.ops.mesh.primitive_cube_add()
obj = bpy.data.objects.get("Cube")
subject.move_object_to_elevation(obj, 3)
assert list(obj.matrix_world.translation) == [0.0, 0.0, 3.0]
class TestRunRootCopyClass(test.bim.bootstrap.NewFile):
def test_nothing(self):
pass
class TestScaleObjectToHeight(test.bim.bootstrap.NewFile):
def test_run(self):
bpy.ops.mesh.primitive_cube_add()
obj = bpy.data.objects.get("Cube")
subject.set_object_origin_to_bottom(obj)
subject.scale_object_to_height(obj, 3)
assert obj.dimensions[2] == 3.0
assert list(obj.scale) == [1.0, 1.0, 1.0]
class TestMarkObjectAsEdited(test.bim.bootstrap.NewFile):
def test_run(self):
obj = bpy.data.objects.new("Cube", None)
subject.mark_object_as_edited(obj)
assert obj in IfcStore.edited_objs
class TestSplitObjectsWithCutter(test.bim.bootstrap.NewFile):
def test_run(self):
bpy.ops.mesh.primitive_cube_add()
bpy.ops.mesh.primitive_plane_add(size=4)
obj = bpy.data.objects.get("Cube")
cutter = bpy.data.objects.get("Plane")
assert obj.dimensions[2] == 2
new_objs = subject.split_objects_with_cutter([obj], cutter)
assert new_objs[0].name == "Cube.001"
assert obj.dimensions[2] == 1
assert new_objs[0].dimensions[2] == 1
+668
View File
@@ -0,0 +1,668 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.api.material
import ifcopenshell.api.style
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.util.unit
import blenderbim.core.tool
import blenderbim.tool as tool
import numpy as np
import json
from test.bim.bootstrap import NewFile
from blenderbim.tool.model import Model as subject
from ifcopenshell.util.shape_builder import V, ShapeBuilder
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Model)
class TestGenerateOccurrenceName(NewFile):
def test_generating_based_on_class(self):
ifc = ifcopenshell.file()
element_type = ifc.createIfcWallType(Name="Foobar")
bpy.context.scene.BIMModelProperties.occurrence_name_style = "CLASS"
assert subject.generate_occurrence_name(element_type, "IfcWall") == "Wall"
def test_generating_based_on_type_name(self):
ifc = ifcopenshell.file()
element_type = ifc.createIfcWallType()
bpy.context.scene.BIMModelProperties.occurrence_name_style = "TYPE"
assert subject.generate_occurrence_name(element_type, "IfcWall") == "Unnamed"
element_type.Name = "Foobar"
assert subject.generate_occurrence_name(element_type, "IfcWall") == "Foobar"
def test_generating_based_on_a_custom_function(self):
ifc = ifcopenshell.file()
element_type = ifc.createIfcWallType()
bpy.context.scene.BIMModelProperties.occurrence_name_style = "CUSTOM"
bpy.context.scene.BIMModelProperties.occurrence_name_function = '"Foobar"'
assert subject.generate_occurrence_name(element_type, "IfcWall") == "Foobar"
class TestGetBooleans(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
context = ifc.createIfcGeometricRepresentationContext()
element = ifc.createIfcWall()
items = [ifc.createIfcExtrudedAreaSolid(), ifc.createIfcExtrudedAreaSolid()]
representation = ifc.createIfcShapeRepresentation(Items=items, ContextOfItems=context)
tool.Ifc.run("geometry.assign_representation", product=element, representation=representation)
bool1 = set(tool.Ifc.run("geometry.add_boolean", representation=representation, matrix=np.eye(4)))
bool2 = set(tool.Ifc.run("geometry.add_boolean", representation=representation, matrix=np.eye(4)))
assert set(subject.get_booleans(element, representation)) == bool1 | bool2
class TestGetManualBooleans(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
context = ifc.createIfcGeometricRepresentationContext()
element = ifc.createIfcWall()
items = [ifc.createIfcExtrudedAreaSolid(), ifc.createIfcExtrudedAreaSolid()]
representation = ifc.createIfcShapeRepresentation(Items=items, ContextOfItems=context)
tool.Ifc.run("geometry.assign_representation", product=element, representation=representation)
bool1 = set(tool.Ifc.run("geometry.add_boolean", representation=representation, matrix=np.eye(4)))
bool2 = set(tool.Ifc.run("geometry.add_boolean", representation=representation, matrix=np.eye(4)))
assert set(subject.get_booleans(element, representation)) == bool1 | bool2
assert len(subject.get_manual_booleans(element, representation)) == 0
subject.mark_manual_booleans(element, bool1)
assert set(subject.get_manual_booleans(element, representation)) == bool1
class TestMarkManualBooleans(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcWall()
boolean = ifc.createIfcBooleanClippingResult()
subject.mark_manual_booleans(element, [boolean])
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean")
assert pset
value = json.loads(pset["Data"])
assert set(value) == {boolean.id()}
class TestUnmarkManualBooleans(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcWall()
boolean = ifc.createIfcBooleanClippingResult()
boolean2 = ifc.createIfcBooleanClippingResult()
subject.mark_manual_booleans(element, [boolean, boolean2])
subject.unmark_manual_booleans(element, [boolean.id()])
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean")
assert pset
value = json.loads(pset["Data"])
assert set(value) == {boolean2.id()}
class TestStairCalculatedParams(NewFile):
def compare_data(self, pset_data, expected_calculated_data):
calculated_data = subject.get_active_stair_calculated_params(pset_data)
for key, value in expected_calculated_data.items():
assert tool.Cad.is_x(calculated_data[key], value)
def test_run(self):
bpy.ops.bim.create_project()
bpy.ops.mesh.add_clever_stair()
pset_data_base = {
"number_of_treads": 3,
"height": 1.0,
"tread_run": 0.3,
"custom_first_last_tread_run": (0.0, 0.0),
"nosing_length": 0.0,
}
calculated_data_base = {
"Number of Risers": 4,
"Tread Rise": 0.25,
"Length": 1.2,
}
self.compare_data(pset_data_base, calculated_data_base)
# custom first and last treads run
pset_data = pset_data_base.copy()
calculated_data = calculated_data_base.copy()
pset_data["custom_first_last_tread_run"] = (0.1, 0.4)
calculated_data["Length"] += -0.2 + 0.1
self.compare_data(pset_data, calculated_data)
# overlap affects stair length only by first tread
pset_data = pset_data_base.copy()
calculated_data = calculated_data_base.copy()
pset_data["nosing_length"] = 0.1
calculated_data["Length"] += 0.1
self.compare_data(pset_data, calculated_data)
# tread gap
pset_data = pset_data_base.copy()
calculated_data = calculated_data_base.copy()
pset_data["nosing_length"] = -0.1
calculated_data["Length"] += 0.1 * pset_data["number_of_treads"]
self.compare_data(pset_data, calculated_data)
class TestGenerateStair2DProfile(NewFile):
def compare_data(self, generated_profile, expected_profile):
verts_gen, edges_gen, faces_gen = generated_profile
verts, edges, faces = expected_profile
assert np.all(edges == np.array(edges_gen))
assert faces == tuple(tuple(face) for face in faces_gen)
for vert, vert_gen in zip(verts, verts_gen, strict=True):
assert tool.Cad.are_vectors_equal(vert, vert_gen, 0.01)
def test_create_concrete_stair(self):
kwargs = {
"base_slab_depth": 0.25,
"has_top_nib": False,
"height": 1.0,
"number_of_treads": 3,
"stair_type": "CONCRETE",
"top_slab_depth": 0.25,
"tread_depth": 0.25,
"tread_run": 0.3,
"width": 1.2,
}
verts_data = (
V(0.0, 0, 0.0),
V(0.0, 0, 0.25),
V(0.3, 0, 0.25),
V(0.3, 0, 0.5),
V(0.6, 0, 0.5),
V(0.6, 0, 0.75),
V(0.9, 0, 0.75),
V(0.9, 0, 1.0),
V(1.2, 0, 1.0),
V(1.2, 0, 0.67457),
V(0.1, 0, -0.25),
V(0.0, 0, -0.25),
)
edges_data = (
(0, 1),
(1, 2),
(2, 3),
(3, 4),
(4, 5),
(5, 6),
(6, 7),
(7, 8),
(8, 9),
(11, 0),
(10, 11),
(9, 10),
)
edges_data = [e[::-1] for e in edges_data]
faces_data = ()
expected_profile = (verts_data, edges_data, faces_data)
generated_profile = subject.generate_stair_2d_profile(**kwargs)
self.compare_data(generated_profile, expected_profile)
def test_create_concrete_stair_nib(self):
kwargs = {
"base_slab_depth": 0.25,
"has_top_nib": True,
"height": 1.0,
"number_of_treads": 3,
"stair_type": "CONCRETE",
"top_slab_depth": 0.25,
"tread_depth": 0.25,
"tread_run": 0.3,
"width": 1.2,
}
verts_data = (
V(0.0, 0, 0.0),
V(0.0, 0, 0.25),
V(0.3, 0, 0.25),
V(0.3, 0, 0.5),
V(0.6, 0, 0.5),
V(0.6, 0, 0.75),
V(0.9, 0, 0.75),
V(0.9, 0, 1.0),
V(1.2, 0, 1.0),
V(1.2, 0, 0.75),
V(1.3, 0, 0.75),
V(0.1, 0, -0.25),
V(0.0, 0, -0.25),
)
edges_data = (
(0, 1),
(1, 2),
(2, 3),
(3, 4),
(4, 5),
(5, 6),
(6, 7),
(7, 8),
(8, 9),
(9, 10),
(12, 0),
(11, 12),
(10, 11),
)
edges_data = [e[::-1] for e in edges_data]
faces_data = ()
expected_profile = (verts_data, edges_data, faces_data)
generated_profile = subject.generate_stair_2d_profile(**kwargs)
self.compare_data(generated_profile, expected_profile)
def test_create_wood_steel_stair(self):
kwargs = {
"height": 1.0,
"number_of_treads": 3,
"stair_type": "WOOD/STEEL",
"tread_depth": 0.25,
"tread_run": 0.3,
"width": 1.2,
}
verts_data = (
V(0.0, 0, 0.0),
V(0.3, 0, 0.0),
V(0.3, 0, 0.25),
V(0.0, 0, 0.25),
V(0.3, 0, 0.25),
V(0.6, 0, 0.25),
V(0.6, 0, 0.5),
V(0.3, 0, 0.5),
V(0.6, 0, 0.5),
V(0.9, 0, 0.5),
V(0.9, 0, 0.75),
V(0.6, 0, 0.75),
V(0.9, 0, 0.75),
V(1.2, 0, 0.75),
V(1.2, 0, 1.0),
V(0.9, 0, 1.0),
)
edges_data = (
(0, 1),
(1, 2),
(2, 3),
(3, 0),
(4, 5),
(5, 6),
(6, 7),
(7, 4),
(8, 9),
(9, 10),
(10, 11),
(11, 8),
(12, 13),
(13, 14),
(14, 15),
(15, 12),
)
faces_data = ()
expected_profile = (verts_data, edges_data, faces_data)
generated_profile = subject.generate_stair_2d_profile(**kwargs)
self.compare_data(generated_profile, expected_profile)
def test_create_generic_stair(self):
kwargs = {"height": 1.0, "number_of_treads": 3, "stair_type": "GENERIC", "tread_run": 0.3, "width": 1.2}
verts_data = (
V(0.0, 0, 0.0),
V(0.0, 0, 0.25),
V(0.3, 0, 0.25),
V(0.3, 0, 0.5),
V(0.6, 0, 0.5),
V(0.6, 0, 0.75),
V(0.9, 0, 0.75),
V(0.9, 0, 1.0),
V(1.2, 0, 1.0),
V(1.2, 0, 0.0),
)
edges_data = (
(0, 1),
(1, 2),
(2, 3),
(3, 4),
(4, 5),
(5, 6),
(6, 7),
(7, 8),
(8, 9),
(9, 0),
)
edges_data = [e[::-1] for e in edges_data]
faces_data = ()
expected_profile = (verts_data, edges_data, faces_data)
generated_profile = subject.generate_stair_2d_profile(**kwargs)
self.compare_data(generated_profile, expected_profile)
class TestUsingArrays(NewFile):
def setup_array(self, add_second_layer=False, sync_children=False):
bpy.context.scene.BIMProjectProperties.template_file = "0"
bpy.ops.bim.create_project()
bpy.ops.mesh.primitive_cube_add()
obj = bpy.context.active_object
bpy.context.scene.BIMRootProperties.ifc_product = "IfcElement"
bpy.ops.bim.assign_class(ifc_class="IfcActuator", predefined_type="ELECTRICACTUATOR", userdefined_type="")
bpy.ops.bim.add_array()
bpy.ops.bim.enable_editing_array(item=0)
obj.BIMArrayProperties.count = 4
obj.BIMArrayProperties.x = 4
obj.BIMArrayProperties.sync_children = sync_children
bpy.ops.bim.edit_array(item=0)
if add_second_layer:
bpy.ops.bim.add_array()
bpy.ops.bim.enable_editing_array(item=1)
obj.BIMArrayProperties.count = 3
obj.BIMArrayProperties.y = 4
obj.BIMArrayProperties.sync_children = sync_children
bpy.ops.bim.edit_array(item=1)
def test_remove_array_last_to_first(self):
self.setup_array(add_second_layer=True)
bpy.ops.bim.remove_array(item=1)
assert len(bpy.context.selected_objects) == 4
bpy.ops.bim.remove_array(item=0)
assert len(bpy.context.selected_objects) == 1
def test_remove_array_first_to_last(self):
self.setup_array(add_second_layer=True)
bpy.ops.bim.remove_array(item=0)
assert len(bpy.context.selected_objects) == 3
bpy.ops.bim.remove_array(item=0)
assert len(bpy.context.selected_objects) == 1
def test_apply_array_1_layer(self):
self.setup_array()
bpy.ops.bim.apply_array()
objs = bpy.context.selected_objects
assert len(objs) == 4
# check BBIM_Array psets are removed
for obj in objs:
element = tool.Ifc.get_entity(obj)
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
assert pset is None, (obj, pset)
def test_apply_array_multiple_layers(self):
self.setup_array(add_second_layer=True)
bpy.ops.bim.apply_array() # apply second layer
bpy.ops.bim.apply_array() # apply first layer
objs = bpy.context.selected_objects
assert len(objs) == 12
# check BBIM_Array psets are removed
for obj in objs:
element = tool.Ifc.get_entity(obj)
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
assert pset is None, (obj, pset)
def test_apply_array_with_sync_children(self):
self.setup_array(sync_children=True)
bpy.ops.bim.apply_array()
objs = bpy.context.selected_objects
assert len(objs) == 4
# check BBIM_Array psets are removed
for obj in objs:
element = tool.Ifc.get_entity(obj)
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
assert pset is None, (obj, pset)
class TestApplyIfcMaterialChanges(NewFile):
def get_used_styles(self, obj: bpy.types.Object) -> set[ifcopenshell.entity_instance]:
ifc_file = tool.Ifc.get()
return {
ifc_file.by_id(s.material.BIMStyleProperties.ifc_definition_id) for s in obj.material_slots if s.material
}
def get_mesh(self, obj: bpy.types.Object) -> bpy.types.Mesh:
mesh = obj.data
assert isinstance(mesh, bpy.types.Mesh)
return mesh
def setup_test(self, and_elements: bool = True) -> None:
bpy.context.scene.BIMProjectProperties.template_file = "0"
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
bpy.ops.bim.create_project()
ifc_file = tool.Ifc.get()
# Setup materials and styles.
context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
assert context # Type checker.
red_material = ifcopenshell.api.material.add_material(ifc_file, "Red Material")
bpy.ops.bim.load_styles(style_type="IfcSurfaceStyle")
bpy.ops.bim.enable_adding_presentation_style()
bpy.data.scenes["Scene"].BIMStylesProperties.style_name = "Red"
bpy.ops.bim.add_presentation_style()
red_style = next((i for i in ifc_file.by_type("IfcSurfaceStyle") if i.Name == "Red"))
ifcopenshell.api.style.assign_material_style(ifc_file, red_material, red_style, context)
blue_material = ifcopenshell.api.material.add_material(ifc_file, "Blue Material")
bpy.ops.bim.enable_adding_presentation_style()
bpy.data.scenes["Scene"].BIMStylesProperties.style_name = "Blue"
bpy.ops.bim.add_presentation_style()
blue_style = next((i for i in ifc_file.by_type("IfcSurfaceStyle") if i.Name == "Blue"))
ifcopenshell.api.style.assign_material_style(ifc_file, blue_material, blue_style, context)
bpy.ops.bim.enable_adding_presentation_style()
bpy.data.scenes["Scene"].BIMStylesProperties.style_name = "Green"
bpy.ops.bim.add_presentation_style()
if and_elements:
self.setup_elements()
def setup_elements(self) -> None:
ifc_file = tool.Ifc.get()
blue_material = next((i for i in ifc_file.by_type("IfcMaterial") if i.Name == "Blue Material"))
blue_style = tool.Material.get_style(blue_material)
# Element type.
bpy.ops.mesh.primitive_cube_add(size=10, location=(0, 0, 4))
element_type_obj = bpy.data.objects["Cube"]
bpy.ops.bim.assign_class(ifc_class="IfcActuatorType", predefined_type="ELECTRICACTUATOR", userdefined_type="")
element_type = tool.Ifc.get_entity(element_type_obj)
# Setup occurrences.
relating_type_id = element_type.id()
bpy.ops.bim.add_constr_type_instance(relating_type_id=relating_type_id)
simple = bpy.context.active_object
simple.name = "Simple"
# Occurrence with an opening.
bpy.ops.bim.add_constr_type_instance(relating_type_id=relating_type_id)
with_opening = bpy.context.active_object
with_opening.name = "With Opening"
bpy.ops.bim.add_potential_opening()
tool.Blender.set_objects_selection(
bpy.context, active_object=with_opening, selected_objects=[with_opening, bpy.data.objects["Opening"]]
)
bpy.ops.bim.add_opening()
# Occurrence with a material override.
bpy.ops.bim.add_constr_type_instance(relating_type_id=relating_type_id)
with_material = bpy.context.active_object
with_material.name = "With Material"
tool.Blender.set_objects_selection(bpy.context, active_object=with_material, selected_objects=[with_material])
ifcopenshell.api.material.assign_material(
ifc_file, products=[tool.Ifc.get_entity(with_material)], material=blue_material
)
tool.Material.ensure_material_assigned([tool.Ifc.get_entity(with_material)], material=blue_material)
assert self.get_used_styles(element_type_obj) == set()
for element in ifc_file.by_type("IfcActuator"):
obj = tool.Ifc.get_object(element)
expected = {blue_style} if obj.name == "With Material" else set()
assert self.get_used_styles(obj) == expected
def test_element_type_and_occurrences(self):
self.setup_test()
ifc_file = tool.Ifc.get()
element_type = next(ifc_file.by_type("IfcActuatorType").__iter__())
red_material = next((i for i in ifc_file.by_type("IfcMaterial") if i.Name == "Red Material"))
red_style = tool.Material.get_style(red_material)
blue_style = next((i for i in ifc_file.by_type("IfcSurfaceStyle") if i.Name == "Blue"))
ifcopenshell.api.material.assign_material(ifc_file, material=red_material, products=[element_type])
tool.Material.ensure_material_assigned([element_type], material=red_material)
assert self.get_used_styles(tool.Ifc.get_object(element_type)) == {red_style}
for element in ifc_file.by_type("IfcActuator"):
obj = tool.Ifc.get_object(element)
expected = {blue_style} if obj.name == "With Material" else {red_style}
assert self.get_used_styles(obj) == expected
ifcopenshell.api.material.unassign_material(ifc_file, products=[element_type])
tool.Material.ensure_material_unassigned([element_type])
assert self.get_used_styles(tool.Ifc.get_object(element_type)) == set()
for element in ifc_file.by_type("IfcActuator"):
obj = tool.Ifc.get_object(element)
expected = {blue_style} if obj.name == "With Material" else set()
assert self.get_used_styles(obj) == expected
def test_dont_override_exisiting_styles(self):
self.setup_test()
ifc_file = tool.Ifc.get()
element_type = next(ifc_file.by_type("IfcActuatorType").__iter__())
red_material = next((i for i in ifc_file.by_type("IfcMaterial") if i.Name == "Red Material"))
green_style = next((i for i in ifc_file.by_type("IfcSurfaceStyle") if i.Name == "Green"))
# Occurrence with a style.
element_type_obj = tool.Ifc.get_object(element_type)
tool.Blender.set_objects_selection(
bpy.context, active_object=element_type_obj, selected_objects=[element_type_obj]
)
bpy.ops.bim.assign_style_to_selected(style_id=green_style.id())
ifcopenshell.api.material.assign_material(ifc_file, material=red_material, products=[element_type])
tool.Material.ensure_material_assigned([element_type], material=red_material)
assert self.get_used_styles(tool.Ifc.get_object(element_type)) == {green_style}
for element in ifc_file.by_type("IfcActuator"):
obj = tool.Ifc.get_object(element)
assert self.get_used_styles(obj) == {green_style}
ifcopenshell.api.material.unassign_material(ifc_file, products=[element_type])
tool.Material.ensure_material_unassigned([element_type])
assert self.get_used_styles(tool.Ifc.get_object(element_type)) == {green_style}
for element in ifc_file.by_type("IfcActuator"):
obj = tool.Ifc.get_object(element)
assert self.get_used_styles(obj) == {green_style}
def test_assign_material_to_representation_that_has_2_items_and_1_item_has_a_style(self):
self.setup_test(and_elements=False)
ifc_file = tool.Ifc.get()
red_material = next((i for i in ifc_file.by_type("IfcMaterial") if i.Name == "Red Material"))
red_style = tool.Material.get_style(red_material)
green_style = next((i for i in ifc_file.by_type("IfcSurfaceStyle") if i.Name == "Green"))
bpy.ops.mesh.primitive_cube_add(size=10, location=(0, 0, 4))
obj = bpy.data.objects["Cube"]
bpy.ops.bim.assign_class(ifc_class="IfcActuator", predefined_type="ELECTRICACTUATOR", userdefined_type="")
element = tool.Ifc.get_entity(obj)
builder = ShapeBuilder(ifc_file)
# Change representation that consists of 2 rep items:
# 1 with style and other without.
rep = tool.Geometry.get_active_representation(obj)
assert rep
cube = rep.Items[0]
cube2 = builder.deep_copy(cube)
rep.Items = [cube, cube2]
tool.Style.assign_style_to_representation_item(cube, green_style)
tool.Geometry._reload_representation(obj)
def get_material_indices(mesh: bpy.types.Mesh) -> np.ndarray:
buffer = np.empty(len(mesh.polygons), dtype=np.int32)
mesh.polygons.foreach_get("material_index", buffer)
return buffer
mesh = self.get_mesh(obj)
assert len(mesh.materials) == 2
assert set(mesh.materials) == {bpy.data.materials["Green"], None}
ifcopenshell.api.material.assign_material(ifc_file, products=[element], material=red_material)
tool.Material.ensure_material_assigned([element], material=red_material)
assert self.get_used_styles(obj) == {green_style, red_style}
ifcopenshell.api.material.unassign_material(ifc_file, products=[element])
tool.Material.ensure_material_unassigned([element])
mesh = self.get_mesh(obj)
assert len(mesh.materials) == 2
assert set(mesh.materials) == {bpy.data.materials["Green"], None}
# Test that if style is the same it would just reuse it.
tool.Style.assign_style_to_representation_item(cube, red_style)
tool.Geometry._reload_representation(obj)
mesh = self.get_mesh(obj)
assert len(mesh.materials) == 2
assert set(mesh.materials) == {bpy.data.materials["Red"], None}
ifcopenshell.api.material.assign_material(ifc_file, products=[element], material=red_material)
tool.Material.ensure_material_assigned([element], material=red_material)
assert mesh.materials[:] == [bpy.data.materials["Red"]]
# All polygons are just reassigned to the existing material.
assert set(get_material_indices(mesh)) == {mesh.materials.find("Red")}
ifcopenshell.api.material.unassign_material(ifc_file, products=[element])
tool.Material.ensure_material_unassigned([element])
mesh = self.get_mesh(obj)
assert len(mesh.materials) == 2
assert set(mesh.materials) == {bpy.data.materials["Red"], None}
assert set(get_material_indices(mesh)) == {0, 1}
def test_assign_unassign_overriding_occurrence_material(self):
self.setup_test(and_elements=True)
ifc_file = tool.Ifc.get()
element_type = next(ifc_file.by_type("IfcActuatorType").__iter__())
red_material = next((i for i in ifc_file.by_type("IfcMaterial") if i.Name == "Red Material"))
no_style_material = ifcopenshell.api.material.add_material(ifc_file, "No Style")
obj = bpy.data.objects["Simple"]
element = tool.Ifc.get_entity(obj)
ifcopenshell.api.material.assign_material(ifc_file, material=red_material, products=[element_type])
tool.Material.ensure_material_assigned([element_type], material=red_material)
# Override type material.
ifcopenshell.api.material.assign_material(ifc_file, material=no_style_material, products=[element])
tool.Material.ensure_material_assigned([element], material=no_style_material)
assert self.get_mesh(obj).materials[:] == []
ifcopenshell.api.material.unassign_material(ifc_file, products=[element])
tool.Material.ensure_material_unassigned([element])
assert self.get_mesh(obj).materials[:] == [bpy.data.materials["Red"]]
+74
View File
@@ -0,0 +1,74 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. 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.nest import Nest as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Nest)
class TestCanNest(NewFile):
def test_elements_can_nest(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcElementAssembly()
element_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, element_obj)
subelement = ifc.createIfcBeam()
subelement_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(subelement, subelement_obj)
assert subject.can_nest(element_obj, subelement_obj) is True
def test_unlinked_elements_cannot_nest(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element_obj = bpy.data.objects.new("Object", None)
subelement_obj = bpy.data.objects.new("Object", None)
assert subject.can_nest(element_obj, subelement_obj) is False
class TestDisableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
subject.disable_editing(obj)
assert obj.BIMObjectNestProperties.is_editing is False
class TestEnableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
assert obj.BIMObjectNestProperties.is_editing is True
class TestGetContainer(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcWall()
container = ifc.createIfcBuildingStorey()
ifcopenshell.api.run("spatial.assign_container", ifc, products=[element], relating_structure=container)
assert subject.get_container(element) == container
+571
View File
@@ -0,0 +1,571 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. 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.owner import Owner as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Owner)
class TestAddAddressAttribute(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 TestAddPersonAttribute(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 TestClearActor(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
actor = ifc.createIfcActor()
subject().set_actor(actor)
subject().clear_actor()
assert bpy.context.scene.BIMOwnerProperties.active_actor_id == 0
class TestClearAddress(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 TestClearOrganisation(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 TestClearPerson(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 TestClearRole(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 TestClearUser(NewFile):
def test_run(self):
TestSetUser().test_run()
subject.clear_user()
assert bpy.context.scene.BIMOwnerProperties.active_user_id == 0
class TestExportActorAttributes(NewFile):
def test_exporting_an_actor(self):
TestImportActorAttributes().test_importing_an_actor()
assert subject().export_actor_attributes() == {
"GlobalId": "GlobalId",
"Name": "Name",
"Description": "Description",
"ObjectType": "ObjectType",
}
def test_exporting_an_occupant(self):
TestImportActorAttributes().test_importing_an_occupant()
assert subject().export_actor_attributes() == {
"GlobalId": "GlobalId",
"Name": "Name",
"Description": "Description",
"ObjectType": "ObjectType",
"PredefinedType": "TENANT",
}
class TestExportAddressAttributes(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 TestExportOrganisationAttributes(NewFile):
def test_run(self):
TestImportOrganisationAttributes().test_run()
assert subject().export_organisation_attributes() == {
"Identification": "Identification",
"Name": "Name",
"Description": "Description",
}
class TestExportPersonAttributes(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 TestExportRoleAttributes(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",
}
class TestGetActor(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
actor = ifc.createIfcActor()
subject().set_actor(actor)
assert subject().get_actor() == actor
class TestGetAddress(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 TestGetOrganisation(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 TestGetPerson(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 TestGetRole(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 TestGetUser(NewFile):
def test_run(self):
assert subject.get_user() is None
TestSetUser().test_run()
user = tool.Ifc.get().by_type("IfcPersonAndOrganization")[0]
assert subject.get_user() == user
def test_falling_back_to_any_available_user_ifc2x3(self):
assert subject.get_user() is None
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
user = ifc.createIfcPersonAndOrganization()
assert subject.get_user() == user
user2 = ifc.createIfcPersonAndOrganization()
subject.set_user(user2)
assert subject.get_user() == user2
class TestImportActorAttributes(NewFile):
def test_importing_an_actor(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
actor = ifc.createIfcActor()
actor.GlobalId = "GlobalId"
actor.Name = "Name"
actor.Description = "Description"
actor.ObjectType = "ObjectType"
subject.set_actor(actor)
subject.import_actor_attributes(actor)
props = bpy.context.scene.BIMOwnerProperties
assert props.actor_attributes.get("GlobalId").string_value == "GlobalId"
assert props.actor_attributes.get("Name").string_value == "Name"
assert props.actor_attributes.get("Description").string_value == "Description"
assert props.actor_attributes.get("ObjectType").string_value == "ObjectType"
def test_importing_an_occupant(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
actor = ifc.createIfcOccupant()
actor.GlobalId = "GlobalId"
actor.Name = "Name"
actor.Description = "Description"
actor.ObjectType = "ObjectType"
actor.PredefinedType = "TENANT"
subject.set_actor(actor)
subject.import_actor_attributes(actor)
props = bpy.context.scene.BIMOwnerProperties
assert props.actor_attributes.get("GlobalId").string_value == "GlobalId"
assert props.actor_attributes.get("Name").string_value == "Name"
assert props.actor_attributes.get("Description").string_value == "Description"
assert props.actor_attributes.get("ObjectType").string_value == "ObjectType"
assert props.actor_attributes.get("PredefinedType").enum_value == "TENANT"
class TestImportAddressAttributes(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 TestImportOrganisationAttributes(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 TestImportPersonAttributes(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 TestImportRoleAttributes(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 TestRemoveAddressAttribute(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 TestRemovePersonAttribute(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 TestSetActor(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
actor = ifc.createIfcActor()
subject().set_actor(actor)
assert bpy.context.scene.BIMOwnerProperties.active_actor_id == actor.id()
class TestSetAddress(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 TestSetOrganisation(NewFile):
def test_run(self):
organisation = ifcopenshell.file().createIfcOrganization()
subject().set_organisation(organisation)
assert bpy.context.scene.BIMOwnerProperties.active_organisation_id == organisation.id()
class TestSetPerson(NewFile):
def test_run(self):
person = ifcopenshell.file().createIfcPerson()
subject().set_person(person)
assert bpy.context.scene.BIMOwnerProperties.active_person_id == person.id()
class TestSetRole(NewFile):
def test_run(self):
role = ifcopenshell.file().createIfcActorRole()
subject().set_role(role)
assert bpy.context.scene.BIMOwnerProperties.active_role_id == role.id()
class TestSetUser(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
user = ifc.createIfcPersonAndOrganization()
subject.set_user(user)
assert bpy.context.scene.BIMOwnerProperties.active_user_id == user.id()
+40
View File
@@ -0,0 +1,40 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import os
import bpy
import ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.patch import Patch as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Patch)
class TestRunMigratePatch(NewFile):
def test_run(self):
cwd = os.path.dirname(os.path.realpath(__file__))
infile = os.path.join(cwd, "..", "files", "ifc2x3.ifc")
outfile = os.path.join(cwd, "..", "files", "temp", "ifc2x3-migrated.ifc")
subject.run_migrate_patch(infile, outfile, "IFC4")
ifc = ifcopenshell.open(outfile)
assert ifc.schema == "IFC4"
+198
View File
@@ -0,0 +1,198 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
import tempfile
from test.bim.bootstrap import NewFile
from blenderbim.tool.project import Project as subject
from pathlib import Path
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Project)
class TestAppendAllTypesFromTemplate(NewFile):
def test_nothing(self):
# TODO refactor
pass
class TestCreateEmpty(NewFile):
def test_run(self):
subject.create_empty("Foobar")
assert bpy.data.objects.get("Foobar")
assert not bpy.data.objects.get("Foobar").data
class TestLoadDefaultThumbnails(NewFile):
def test_nothing(self):
pass # Not possible to test this headlessly
class TestRunAggregateAssignObject(NewFile):
def test_nothing(self):
pass
class TestRunContextAddContext(NewFile):
def test_nothing(self):
pass
class TestRunOwnerAddOrganisation(NewFile):
def test_nothing(self):
pass
class TestRunOwnerAddPerson(NewFile):
def test_nothing(self):
pass
class TestRunOwnerAddPersonAndOrganisation(NewFile):
def test_nothing(self):
pass
class TestRunOwnerSetUser(NewFile):
def test_nothing(self):
pass
class TestRunAssignClass(NewFile):
def test_nothing(self):
pass
class TestRunUnitAssignSceneUnits(NewFile):
def test_nothing(self):
pass
class TestSetContext(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
context = ifc.createIfcGeometricRepresentationContext()
subject.set_context(context)
assert bpy.context.scene.BIMRootProperties.contexts == str(context.id())
class TestSetDefaultContext(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifc.createIfcProject()
model = ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
body = ifcopenshell.api.run(
"context.add_context",
ifc,
parent=model,
context_type="Model",
context_identifier="Body",
target_view="MODEL_VIEW",
)
subject.set_default_context()
assert bpy.context.scene.BIMRootProperties.contexts == str(body.id())
class TestSetDefaultModelingDimensions(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifc.createIfcProject()
ifcopenshell.api.run("unit.assign_unit", ifc)
subject.set_default_modeling_dimensions()
props = bpy.context.scene.BIMModelProperties
assert props.extrusion_depth == 3
assert props.length == 1
assert props.rl1 == 0
assert props.rl2 == 1
assert props.x == 0.5
assert props.y == 0.5
assert props.z == 0.5
class PreserveFileContents:
original_content: str
filepath: Path
def __init__(self, filepath: Path):
self.filepath = filepath
def __enter__(self):
if not self.filepath.exists():
self.filepath.parent.mkdir(parents=True, exist_ok=True)
open(self.filepath, "w").close() # touch.
with open(self.filepath, "r") as file:
self.original_content = file.read()
return self
def __exit__(self, exc_type, exc_value, traceback):
with open(self.filepath, "w") as file:
file.write(self.original_content)
class TestRecentIFCProjects(NewFile):
def test_get_recent_ifc_projects_path(self):
assert subject.get_recent_ifc_projects_path().name == "recent-ifc-projects.txt"
def test_clear_recent_ifc_projects(self):
filepath = subject.get_recent_ifc_projects_path()
with PreserveFileContents(filepath):
with open(filepath, "w") as fo:
fo.write(tempfile.NamedTemporaryFile(suffix=".ifc").name)
assert filepath.stat().st_size != 0
subject.clear_recent_ifc_projects()
assert filepath.stat().st_size == 0
def test_get_write_recent_ifc_projects(self):
filepath = subject.get_recent_ifc_projects_path()
with PreserveFileContents(filepath):
subject.clear_recent_ifc_projects()
assert filepath.stat().st_size == 0
projects: list[Path] = []
for _ in range(3):
ifc_file = Path(tempfile.NamedTemporaryFile(suffix=".ifc").name)
open(ifc_file, "w").close()
projects.append(ifc_file)
subject.write_recent_ifc_projects(projects)
assert filepath.stat().st_size != 0
assert subject.get_recent_ifc_projects() == projects
with open(filepath) as fi:
contents = fi.read()
assert contents == "\n".join(str(p) for p in projects)
def test_add_recent_ifc_project(self):
filepath = subject.get_recent_ifc_projects_path()
with PreserveFileContents(filepath):
subject.clear_recent_ifc_projects()
assert filepath.stat().st_size == 0
ifc_file = Path(tempfile.NamedTemporaryFile(suffix=".ifc").name)
subject.add_recent_ifc_project(ifc_file)
assert filepath.stat().st_size != 0
assert subject.get_recent_ifc_projects() == [ifc_file]
+51
View File
@@ -0,0 +1,51 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
from blenderbim.tool.pset import Pset as subject
from test.bim.bootstrap import NewFile
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Pset)
class TestGetElementPset(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcWall()
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo")
assert subject.get_element_pset(element, "Foo") == pset
class TestIsPsetEmpty(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcWall()
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo")
assert subject.is_pset_empty(pset) is True
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
assert subject.is_pset_empty(pset) is False
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": None})
assert subject.is_pset_empty(pset) is True
+217
View File
@@ -0,0 +1,217 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.pset
import test.bim.bootstrap
import blenderbim.core.tool
import blenderbim.core.root
import blenderbim.tool as tool
import blenderbim.bim.import_ifc as import_ifc
import blenderbim.bim.module.qto.calculator as calculator
from blenderbim.tool.qto import Qto as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Qto)
class TestGetRadiusOfSelectedVertices(test.bim.bootstrap.NewFile):
def test_run(self):
bpy.ops.mesh.primitive_circle_add()
assert round(subject.get_radius_of_selected_vertices(bpy.data.objects.get("Circle")), 3) == 1
class TestSetQtoResult(test.bim.bootstrap.NewFile):
def test_run(self):
subject.set_qto_result(123.4567)
assert bpy.context.scene.BIMQtoProperties.qto_result == "123.457"
class TestGetRoundedValue(test.bim.bootstrap.NewFile):
def test_run(self):
quantity = 1.2345
assert subject.get_rounded_value(quantity) == 1.234
class TestGetCalculatedObjectQuantities(test.bim.bootstrap.NewFile):
def setup_file(self):
import logging
self.ifc = ifcopenshell.file()
tool.Ifc.set(self.ifc)
ifcopenshell.api.run("root.create_entity", self.ifc, ifc_class="IfcProject", name="My Project")
ifc_import_settings = import_ifc.IfcImportSettings.factory(
bpy.context, tool.Ifc.get_path(), logging.getLogger("ImportIFC")
)
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
ifc_importer.file = self.ifc
ifc_importer.create_project()
def setup_units(self, units):
ifcopenshell.api.run("unit.assign_unit", self.ifc, **units)
def calculate_quantities(self, obj):
import ifc5d.qto
context = ifcopenshell.api.run("context.add_context", self.ifc, context_type="Model")
bpy.ops.mesh.primitive_cube_add(location=(0.0, 0.0, 0.0), size=2)
obj = bpy.context.active_object
element = blenderbim.core.root.assign_class(
tool.Ifc,
tool.Collector,
tool.Root,
obj=obj,
ifc_class="IfcWall",
predefined_type="ELEMENTEDWALL",
context=context,
)
rules = {
"calculators": {
"Blender": {
"IfcWall": {
"Qto_WallBaseQuantities": {
"GrossFootprintArea": "get_gross_footprint_area",
"GrossSideArea": "get_gross_side_area",
"GrossVolume": "get_gross_volume",
"GrossWeight": "get_gross_weight",
"Height": "get_height",
"Length": "get_length",
"NetFootprintArea": "get_net_footprint_area",
"NetSideArea": "get_net_side_area",
"NetVolume": "get_net_volume",
"NetWeight": "get_net_weight",
"Width": "get_width",
}
},
}
}
}
ifc_file = tool.Ifc.get()
results = ifc5d.qto.quantify(ifc_file, {element}, rules)
return {k: round(v, 3) for k, v in results[element]["Qto_WallBaseQuantities"].items() if v is not None}
def test_meters_project_unit(self):
self.setup_file()
self.setup_units(
{
"length": {"is_metric": True, "raw": "METERS"},
"area": {"is_metric": True, "raw": "SQUARE_METERS"},
"volume": {"is_metric": True, "raw": "CUBIC_METERS"},
}
)
quantities = self.calculate_quantities(bpy.context.active_object)
assert quantities["Length"] == 2
assert quantities["Width"] == 2
assert quantities["Height"] == 2
assert quantities["GrossFootprintArea"] == 4
assert quantities["NetFootprintArea"] == 4
assert quantities["GrossSideArea"] == 4
assert quantities["NetSideArea"] == 4
assert quantities["GrossVolume"] == 8
assert quantities["NetVolume"] == 8
def test_prefix_project_unit(self):
self.setup_file()
self.setup_units(
{
"length": {"is_metric": True, "raw": "MILLIMETERS"},
"area": {"is_metric": True, "raw": "SQUARE_MILLIMETERS"},
"volume": {"is_metric": True, "raw": "CUBIC_MILLIMETERS"},
}
)
quantities = self.calculate_quantities(bpy.context.active_object)
assert quantities["Length"] == 2e3
assert quantities["Width"] == 2e3
assert quantities["Height"] == 2e3
assert quantities["GrossFootprintArea"] == 4e6
assert quantities["NetFootprintArea"] == 4e6
assert quantities["GrossSideArea"] == 4e6
assert quantities["NetSideArea"] == 4e6
assert quantities["GrossVolume"] == 8e9
assert quantities["NetVolume"] == 8e9
def test_imperial_project_unit(self):
self.setup_file()
self.setup_units(
{
"length": {"is_metric": False, "raw": "FEET"},
"area": {"is_metric": False, "raw": "FEET"},
"volume": {"is_metric": False, "raw": "FEET"},
}
)
quantities = self.calculate_quantities(bpy.context.active_object)
assert quantities["Length"] == 6.562
assert quantities["Width"] == 6.562
assert quantities["Height"] == 6.562
assert quantities["GrossFootprintArea"] == 43.056
assert quantities["NetFootprintArea"] == 43.056
assert quantities["GrossSideArea"] == 43.056
assert quantities["NetSideArea"] == 43.056
assert quantities["GrossVolume"] == 282.517
assert quantities["NetVolume"] == 282.517
class TestGetBaseQto(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
wall = ifc.createIfcWall()
wall_obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
tool.Ifc.link(wall, wall_obj)
product = tool.Ifc.get_entity(wall_obj)
pset_qto = ifcopenshell.api.run("pset.add_qto", ifc, product=wall, name="Qto_Basefoo")
assert subject.get_base_qto(product).id() == pset_qto.get_info()["id"]
assert subject.get_base_qto(product).Name == pset_qto.Name
def test_no_quantities(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
wall = ifc.createIfcWall()
wall_obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
tool.Ifc.link(wall, wall_obj)
product = tool.Ifc.get_entity(wall_obj)
assert not subject.get_base_qto(product) == True
class TestGetRelatedCostItemQuantities(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
wall = ifc.createIfcWall()
product = tool.Ifc.get_entity(wall)
schedule = ifcopenshell.api.run("cost.add_cost_schedule", ifc)
item = ifcopenshell.api.run("cost.add_cost_item", ifc, cost_schedule=schedule)
ifcopenshell.api.run("cost.edit_cost_item", ifc, cost_item=item, attributes={"Name": "Foo"})
qto = ifcopenshell.api.run("pset.add_qto", ifc, product=wall, name="Qto_WallBaseQuantities")
ifcopenshell.api.run("pset.edit_qto", ifc, qto=qto, properties={"NetVolume": 42.0})
ifcopenshell.api.run(
"cost.assign_cost_item_quantity", ifc, cost_item=item, products=[wall], prop_name="NetVolume"
)
assert subject.get_related_cost_item_quantities(wall)[0]["cost_item_name"] == "Foo"
assert subject.get_related_cost_item_quantities(wall)[0]["quantity_name"] == "NetVolume"
assert subject.get_related_cost_item_quantities(wall)[0]["quantity_value"] == 42
assert subject.get_related_cost_item_quantities(wall)[0]["quantity_type"] == "IfcQuantityVolume"
+200
View File
@@ -0,0 +1,200 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.api
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.root import Root as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Root)
class TestAddTrackedOpening(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.add_tracked_opening(obj)
props = bpy.context.scene.BIMModelProperties
assert props.openings[0].obj == obj
class TestCopyRepresentation(NewFile):
def test_copying_a_product(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
source = ifc.createIfcWall(Representation=ifc.createIfcProductDefinitionShape())
dest = ifc.createIfcWall()
subject.copy_representation(source, dest)
assert dest.Representation.is_a("IfcProductDefinitionShape")
def test_copying_a_type_product(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
source = ifc.createIfcWallType(RepresentationMaps=[ifc.createIfcRepresentationMap()])
dest = ifc.createIfcWallType()
subject.copy_representation(source, dest)
assert dest.RepresentationMaps[0].is_a("IfcRepresentationMap")
class TestDoesTypeHaveRepresentations(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
element = ifc.createIfcWallType()
assert subject.does_type_have_representations(element) is False
element.RepresentationMaps = [ifc.createIfcRepresentationMap()]
assert subject.does_type_have_representations(element) is True
class TestGetDecompositionRelationships(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcWall()
opening = ifc.createIfcOpeningElement()
fill = ifc.createIfcWindow()
ifcopenshell.api.run("void.add_opening", ifc, opening=opening, element=element)
ifcopenshell.api.run("void.add_filling", ifc, opening=opening, element=fill)
obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(fill, obj)
assert subject.get_decomposition_relationships([obj]) == {fill: {"type": "fill", "element": element}}
class TestGetElementRepresentation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
context = ifc.createIfcGeometricRepresentationContext(ContextType="Model")
representation = ifc.createIfcShapeRepresentation(ContextOfItems=context)
wall = ifc.createIfcWall(Representation=ifc.createIfcProductDefinitionShape(Representations=[representation]))
assert subject.get_element_representation(wall, context) == representation
class TestGetElementType(NewFile):
def test_run(self):
bpy.ops.bim.create_project()
ifc = tool.Ifc.get()
element = ifc.createIfcWall()
type = ifc.createIfcWallType()
ifcopenshell.api.run("type.assign_type", ifc, related_objects=[element], relating_type=type)
assert subject.get_element_type(element) == type
class TestGetObjectName(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
assert subject.get_object_name(obj) == "Object"
def test_blender_number_suffixes_are_ignored(self):
obj = bpy.data.objects.new("Object.001", None)
assert subject.get_object_name(obj) == "Object"
obj = bpy.data.objects.new("Object.foo.123", None)
assert subject.get_object_name(obj) == "Object.foo"
class TestGetObjectRepresentation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
representation = ifc.createIfcShapeRepresentation()
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
obj.data.BIMMeshProperties.ifc_definition_id = representation.id()
assert subject.get_object_representation(obj) == representation
class TestGetRepresentationContext(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
context = ifc.createIfcGeometricRepresentationContext()
representation = ifc.createIfcShapeRepresentation(ContextOfItems=context)
assert subject.get_representation_context(representation) == context
class TestIsElementA(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
assert subject.is_element_a(ifc.createIfcWall(), "IfcSlab") is False
assert subject.is_element_a(ifc.createIfcOpeningElement(), "IfcOpeningElement") is True
class TestLinkObjectData(NewFile):
def test_run(self):
data = bpy.data.meshes.new("Mesh")
source = bpy.data.objects.new("Object", data)
destination = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
subject.link_object_data(source, destination)
assert source.data == data
assert source.data == destination.data
class TestRunGeometryAddRepresntation(NewFile):
def test_nothing(self):
pass
class TestSetObjectName(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
element = ifc.createIfcWall()
subject.set_object_name(obj, element)
assert obj.name == "IfcWall/Unnamed"
def test_existing_blender_names_are_ignored(self):
ifc = ifcopenshell.file()
obj = bpy.data.objects.new("IfcSlab/Object", bpy.data.meshes.new("Mesh"))
element = ifc.createIfcWall()
element.Name = "Foobar"
subject.set_object_name(obj, element)
assert obj.name == "IfcWall/Foobar"
class TestReassignClass(NewFile):
def test_run(self):
bpy.context.scene.BIMProjectProperties.template_file = "IFC4 Demo Template.ifc"
bpy.ops.bim.create_project()
ifc_file = tool.Ifc.get()
context = bpy.context
relating_type_id = ifc_file.by_type("IfcSlabType")[0].id()
n_wall_types = len(ifc_file.by_type("IfcWallType"))
n_slab_types = len(ifc_file.by_type("IfcSlabType"))
# create 3 slabs
bpy.ops.bim.add_constr_type_instance(relating_type_id=relating_type_id)
bpy.ops.bim.add_constr_type_instance(relating_type_id=relating_type_id)
bpy.ops.bim.add_constr_type_instance(relating_type_id=relating_type_id)
slabs = [tool.Ifc.get_object(e) for e in ifc_file.by_type("IfcSlab")]
assert len(slabs) == 3
tool.Blender.set_objects_selection(context, slabs[0], (slabs[1],))
context.scene.BIMRootProperties.ifc_product = "IfcElement"
context.scene.BIMRootProperties.ifc_class = "IfcWall"
bpy.ops.bim.reassign_class()
assert len(ifc_file.by_type("IfcWall")) == 3
assert len(ifc_file.by_type("IfcSlab")) == 0
assert len(ifc_file.by_type("IfcWallType")) == n_wall_types + 1
assert len(ifc_file.by_type("IfcSlabType")) == n_slab_types - 1
+231
View File
@@ -0,0 +1,231 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
from blenderbim.tool.spatial import Spatial as subject
from test.bim.bootstrap import NewFile
from mathutils import Matrix
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Spatial)
class TestCanContain(NewFile):
def test_a_spatial_structure_element_can_contain_an_element(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
structure = ifc.createIfcSite()
structure_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(structure, structure_obj)
element = ifc.createIfcWall()
element_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, element_obj)
assert subject.can_contain(structure, element_obj) is True
def test_a_spatial_structure_element_can_contain_an_element_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
structure = ifc.createIfcSite()
structure_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(structure, structure_obj)
element = ifc.createIfcWall()
element_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, element_obj)
assert subject.can_contain(structure, element_obj) is True
def test_a_spatial_zone_element_cannot_contain_an_element(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
structure = ifc.createIfcSpatialZone()
structure_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(structure, structure_obj)
element = ifc.createIfcWall()
element_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, element_obj)
assert subject.can_contain(structure, element_obj) is False
def test_unlinked_elements_cannot_contain_anything(self):
structure_obj = bpy.data.objects.new("Object", None)
element_obj = bpy.data.objects.new("Object", None)
assert subject.can_contain(structure_obj, element_obj) is False
def test_a_non_spatial_element_cannot_contain_anything(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
structure = ifc.createIfcWall()
structure_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(structure, structure_obj)
element = ifc.createIfcWall()
element_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, element_obj)
assert subject.can_contain(structure, element_obj) is False
def test_a_non_element_cannot_be_contained(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
structure = ifc.createIfcSite()
structure_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(structure, structure_obj)
element = ifc.createIfcTask()
element_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, element_obj)
assert subject.can_contain(structure, element_obj) is False
def test_other_non_elements_that_have_a_contained_in_structure_attribute_can_be_contained(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
structure = ifc.createIfcSite()
structure_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(structure, structure_obj)
element = ifc.createIfcGrid()
element_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, element_obj)
assert subject.can_contain(structure, element_obj) is True
class TestCanReference(NewFile):
def test_an_element_can_reference_a_spatial_element(self):
ifc = ifcopenshell.file()
assert subject.can_reference(ifc.createIfcSite(), ifc.createIfcWall()) is True
def test_an_element_can_reference_a_spatial_element_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
assert subject.can_reference(ifc.createIfcSite(), ifc.createIfcWall()) is True
def test_a_non_spatial_element_cannot_reference_anything(self):
ifc = ifcopenshell.file()
assert subject.can_reference(ifc.createIfcWall(), ifc.createIfcWall()) is False
def test_a_non_element_cannot_reference_anything(self):
ifc = ifcopenshell.file()
assert subject.can_reference(ifc.createIfcSite(), ifc.createIfcTask()) is False
class TestDisableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
subject.disable_editing(obj)
assert obj.BIMObjectSpatialProperties.is_editing is False
class TestDuplicateObjectAndData(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
new_obj = subject.duplicate_object_and_data(obj)
assert new_obj != obj
assert new_obj.data != obj.data
obj = bpy.data.objects.new("Object", None)
new_obj = subject.duplicate_object_and_data(obj)
assert new_obj != obj
assert new_obj.data is None
class TestEnableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
assert obj.BIMObjectSpatialProperties.is_editing is True
class TestGetContainer(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
site = ifc.createIfcSite()
wall = ifc.createIfcWall()
ifcopenshell.api.run("spatial.assign_container", ifc, products=[wall], relating_structure=site)
assert subject.get_container(wall) == site
class TestGetDecomposedElements(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
site = ifc.createIfcSite()
wall = ifc.createIfcWall()
ifcopenshell.api.run("spatial.assign_container", ifc, products=[wall], relating_structure=site)
assert subject.get_decomposed_elements(site) == [wall]
class TestGetObjectMatrix(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
assert subject.get_object_matrix(obj) == obj.matrix_world
class TestGetRelativeObjectMatrix(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
relative_obj = bpy.data.objects.new("Object", None)
relative_obj.matrix_world[0][3] = 1
assert subject.get_relative_object_matrix(obj, relative_obj)[0][3] == -1
class TestRunRootCopyClass(NewFile):
def test_nothing(self):
pass
class TestRunSpatialAssignContainer(NewFile):
def test_nothing(self):
pass
class TestSelectObject(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
bpy.context.scene.collection.objects.link(obj)
subject.select_object(obj)
assert obj in bpy.context.selected_objects
class TestSetActiveObject(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
bpy.context.scene.collection.objects.link(obj)
subject.set_active_object(obj)
assert bpy.context.view_layer.objects.active == obj
assert obj in bpy.context.selected_objects
class TestSetRelativeObjectMatrix(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
relative_obj = bpy.data.objects.new("Object", None)
relative_obj.matrix_world[0][3] = 1
matrix = Matrix()
matrix[0][3] = 1
subject.set_relative_object_matrix(obj, relative_obj, matrix)
assert obj.matrix_world[0][3] == 2
class TestSelectProducts(NewFile):
def test_select_products(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
product = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
obj = bpy.data.objects.new("Object", None)
bpy.context.scene.collection.objects.link(obj)
tool.Ifc.link(product, obj)
subject.select_products([product])
assert obj in bpy.context.selected_objects
+498
View File
@@ -0,0 +1,498 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import os
import bpy
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.representation
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.style import Style as subject
from ifcopenshell.util.shape_builder import ShapeBuilder
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Style)
class TestCanSupportRenderingStyle(NewFile):
def test_anything_with_nodes_can_support_a_rendering_style(self):
obj = bpy.data.materials.new("Material")
obj.use_nodes = True
assert subject.can_support_rendering_style(obj) is True
def test_without_nodes_we_do_not_support_rendering(self):
obj = bpy.data.materials.new("Material")
obj.use_nodes = False
assert subject.can_support_rendering_style(obj) is False
class TestDisableEditing(NewFile):
def test_run(self):
props = bpy.context.scene.BIMStylesProperties
props.is_editing_style = 1
subject.disable_editing()
assert props.is_editing_style == 0
class TestDisableEditingStyles(NewFile):
def test_run(self):
bpy.context.scene.BIMStylesProperties.is_editing = True
subject.disable_editing_styles()
assert bpy.context.scene.BIMStylesProperties.is_editing is False
class TestEnableEditing(NewFile):
def test_run(self):
props = bpy.context.scene.BIMStylesProperties
style = ifcopenshell.file().create_entity("IfcSurfaceStyle")
subject.enable_editing(style)
assert props.is_editing_style is style.id()
class TestEnableEditingStyles(NewFile):
def test_run(self):
bpy.context.scene.BIMStylesProperties.is_editing = False
subject.enable_editing_styles()
assert bpy.context.scene.BIMStylesProperties.is_editing is True
class TestExportSurfaceAttributes(NewFile):
def test_run(self):
TestImportSurfaceAttributes().test_run()
assert subject.export_surface_attributes() == {"Name": "Name", "Side": "BOTH"}
class TestGetActiveStyleType(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
bpy.context.scene.BIMStylesProperties.style_type = "IfcSurfaceStyle"
assert subject.get_active_style_type() == "IfcSurfaceStyle"
bpy.context.scene.BIMStylesProperties.style_type = "IfcCurveStyle"
assert subject.get_active_style_type() == "IfcCurveStyle"
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() == context
class TestGetElementsByStyle(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
style = ifc.createIfcSurfaceStyle()
item = ifc.createIfcExtrudedAreaSolid()
ifc.createIfcStyledItem(Item=item, Styles=[style])
element.Representation = ifc.createIfcProductDefinitionShape(
Representations=[ifc.createIfcShapeRepresentation(Items=[item])]
)
assert subject.get_elements_by_style(style) == {element}
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.BIMStyleProperties.ifc_definition_id = style.id()
assert subject.get_style(obj) == style
def test_getting_nothing_for_a_broken_link_style(self):
obj = bpy.data.materials.new("Material")
obj.BIMStyleProperties.ifc_definition_id = 1
assert subject.get_style(obj) == None
class TestGetSurfaceRenderingAttributes(NewFile):
def test_get_different_surface_and_diffuse_colours_from_a_principled_bsdf(self):
obj = bpy.data.materials.new("Material")
obj.diffuse_color = [1, 1, 1, 1]
obj.use_nodes = True
node = tool.Blender.get_material_node(obj, "BSDF_PRINCIPLED")
node.inputs["Alpha"].default_value = 0.8
node.inputs["Base Color"].default_value = [0.5, 0.5, 0.5, 0.5]
node.inputs["Roughness"].default_value = 0.2
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": 0.5,
"Green": 0.5,
"Blue": 0.5,
},
"SpecularColour": 0.0,
"SpecularHighlight": {"IfcSpecularRoughness": 0.2},
"ReflectanceMethod": "NOTDEFINED",
}
def test_get_rendering_styles_from_a_glossy_bsdf(self):
obj = bpy.data.materials.new("Material")
obj.diffuse_color = [1, 1, 1, 1]
obj.use_nodes = True
output = tool.Blender.get_material_node(obj, "OUTPUT_MATERIAL")
node = tool.Blender.get_material_node(obj, "BSDF_PRINCIPLED")
obj.node_tree.nodes.remove(node)
node = obj.node_tree.nodes.new(type="ShaderNodeBsdfGlossy")
node.inputs["Color"].default_value = [0.5, 0.5, 0.5, 0.5]
node.inputs["Roughness"].default_value = 0.2
obj.node_tree.links.new(node.outputs[0], output.inputs[0])
assert subject.get_surface_rendering_attributes(obj) == {
"SurfaceColour": {
"Name": None,
"Red": 1,
"Green": 1,
"Blue": 1,
},
"Transparency": 0,
"DiffuseColour": {
"Name": None,
"Red": 0.5,
"Green": 0.5,
"Blue": 0.5,
},
"SpecularHighlight": {"IfcSpecularRoughness": 0.2},
"ReflectanceMethod": "METAL",
}
def test_get_rendering_styles_from_a_diffuse_bsdf(self):
obj = bpy.data.materials.new("Material")
obj.diffuse_color = [1, 1, 1, 1]
obj.use_nodes = True
output = tool.Blender.get_material_node(obj, "OUTPUT_MATERIAL")
node = tool.Blender.get_material_node(obj, "BSDF_PRINCIPLED")
obj.node_tree.nodes.remove(node)
node = obj.node_tree.nodes.new(type="ShaderNodeBsdfDiffuse")
node.inputs["Color"].default_value = [0.5, 0.5, 0.5, 0.5]
node.inputs["Roughness"].default_value = 0.2
obj.node_tree.links.new(node.outputs[0], output.inputs[0])
assert subject.get_surface_rendering_attributes(obj) == {
"SurfaceColour": {
"Name": None,
"Red": 1,
"Green": 1,
"Blue": 1,
},
"Transparency": 0,
"DiffuseColour": {
"Name": None,
"Red": 0.5,
"Green": 0.5,
"Blue": 0.5,
},
"SpecularHighlight": {"IfcSpecularRoughness": 0.2},
"ReflectanceMethod": "MATT",
}
def test_get_rendering_styles_from_a_glass_bsdf(self):
obj = bpy.data.materials.new("Material")
obj.diffuse_color = [1, 1, 1, 1]
obj.use_nodes = True
output = tool.Blender.get_material_node(obj, "OUTPUT_MATERIAL")
node = tool.Blender.get_material_node(obj, "BSDF_PRINCIPLED")
obj.node_tree.nodes.remove(node)
node = obj.node_tree.nodes.new(type="ShaderNodeBsdfGlass")
node.inputs["Color"].default_value = [0.5, 0.5, 0.5, 0.5]
node.inputs["Roughness"].default_value = 0.2
obj.node_tree.links.new(node.outputs[0], output.inputs[0])
assert subject.get_surface_rendering_attributes(obj) == {
"SurfaceColour": {
"Name": None,
"Red": 1,
"Green": 1,
"Blue": 1,
},
"Transparency": 0,
"DiffuseColour": {
"Name": None,
"Red": 0.5,
"Green": 0.5,
"Blue": 0.5,
},
"SpecularHighlight": {"IfcSpecularRoughness": 0.2},
"ReflectanceMethod": "GLASS",
}
def test_get_rendering_styles_from_a_emission_bsdf(self):
obj = bpy.data.materials.new("Material")
obj.diffuse_color = [1, 1, 1, 1]
obj.use_nodes = True
output = tool.Blender.get_material_node(obj, "OUTPUT_MATERIAL")
node = tool.Blender.get_material_node(obj, "BSDF_PRINCIPLED")
obj.node_tree.nodes.remove(node)
node = obj.node_tree.nodes.new(type="ShaderNodeEmission")
node.inputs["Color"].default_value = [0.5, 0.5, 0.5, 0.5]
obj.node_tree.links.new(node.outputs[0], output.inputs[0])
assert subject.get_surface_rendering_attributes(obj) == {
"SurfaceColour": {
"Name": None,
"Red": 1,
"Green": 1,
"Blue": 1,
},
"Transparency": 0,
"DiffuseColour": {
"Name": None,
"Red": 0.5,
"Green": 0.5,
"Blue": 0.5,
},
"SpecularHighlight": None,
"ReflectanceMethod": "FLAT",
}
def test_other_unsupported_bsdfs_copy_the_rendering_style_from_the_shading_colours_as_a_fallback(self):
obj = bpy.data.materials.new("Material")
obj.diffuse_color = [1, 1, 1, 1]
obj.use_nodes = True
output = tool.Blender.get_material_node(obj, "OUTPUT_MATERIAL")
node = tool.Blender.get_material_node(obj, "BSDF_PRINCIPLED")
obj.node_tree.nodes.remove(node)
node = obj.node_tree.nodes.new(type="ShaderNodeVolumePrincipled")
node.inputs["Color"].default_value = [0.5, 0.5, 0.5, 0.5]
obj.node_tree.links.new(node.outputs[0], output.inputs[0])
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,
},
"SpecularHighlight": None,
"ReflectanceMethod": "NOTDEFINED",
}
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.BIMStyleProperties.ifc_definition_id = style.id()
assert subject.get_surface_rendering_style(obj) == style_item
class TestGetSurfaceShadingAttributes(NewFile):
def test_get_colours_from_a_basic_material(self):
tool.Ifc.set(ifcopenshell.file())
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,
}
def test_get_colours_from_a_basic_material_ifc2x3(self):
tool.Ifc.set(ifcopenshell.file(schema="IFC2X3"))
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,
},
}
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.BIMStyleProperties.ifc_definition_id = style.id()
assert subject.get_surface_shading_style(obj) == style_item
def test_do_not_get_rendering_styles(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.BIMStyleProperties.ifc_definition_id = style.id()
assert subject.get_surface_shading_style(obj) is None
class TestGetSurfaceTextureStyle(NewFile):
def test_run(self):
tool.Ifc.set(ifcopenshell.file())
style_item = tool.Ifc.get().createIfcSurfaceStyleWithTextures()
style = tool.Ifc.get().createIfcSurfaceStyle(Styles=[style_item])
obj = bpy.data.materials.new("Material")
obj.BIMStyleProperties.ifc_definition_id = style.id()
assert subject.get_surface_texture_style(obj) == style_item
class TestGetUVMaps(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
item = ifc.createIfcTriangulatedFaceSet()
uv_map = ifc.createIfcIndexedTriangleTextureMap(MappedTo=item)
representation = ifc.createIfcShapeRepresentation(Items=[item])
assert subject.get_uv_maps(representation) == [uv_map]
class TestImportSurfaceAttributes(NewFile):
def test_run(self):
tool.Ifc.set(ifc := ifcopenshell.file())
props = bpy.context.scene.BIMStylesProperties
style = ifc.create_entity("IfcSurfaceStyle", "Name", "BOTH")
subject.import_surface_attributes(style)
assert props.attributes.get("Name").string_value == "Name"
assert props.attributes.get("Side").enum_value == "BOTH"
def test_importing_surface_attributes_twice(self):
tool.Ifc.set(ifc := ifcopenshell.file())
style = ifc.create_entity("IfcSurfaceStyle", "Name", "BOTH")
props = bpy.context.scene.BIMStylesProperties
subject.import_surface_attributes(style)
assert len(props.attributes) == 2
assert props.attributes.get("Name").string_value == "Name"
assert props.attributes.get("Side").enum_value == "BOTH"
subject.import_surface_attributes(style)
assert len(props.attributes) == 2
assert props.attributes.get("Name").string_value == "Name"
assert props.attributes.get("Side").enum_value == "BOTH"
class TestImportPresentationStyles(NewFile):
def test_import_curve_styles(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
style = ifc.createIfcCurveStyle(Name="Name")
subject.import_presentation_styles("IfcCurveStyle")
props = bpy.context.scene.BIMStylesProperties
assert props.styles[0].ifc_definition_id == style.id()
assert props.styles[0].name == "Name"
assert props.styles[0].total_elements == 0
def test_import_fillarea_styles(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
style = ifc.createIfcFillAreaStyle(Name="Name")
subject.import_presentation_styles("IfcFillAreaStyle")
props = bpy.context.scene.BIMStylesProperties
assert props.styles[0].ifc_definition_id == style.id()
assert props.styles[0].name == "Name"
assert props.styles[0].total_elements == 0
def test_import_surface_styles(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
style = ifc.createIfcSurfaceStyle(Name="Name")
subject.import_presentation_styles("IfcSurfaceStyle")
props = bpy.context.scene.BIMStylesProperties
assert props.styles[0].ifc_definition_id == style.id()
assert props.styles[0].name == "Name"
assert props.styles[0].total_elements == 0
def test_import_text_styles(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
style = ifc.createIfcTextStyle(Name="Name")
subject.import_presentation_styles("IfcTextStyle")
props = bpy.context.scene.BIMStylesProperties
assert props.styles[0].ifc_definition_id == style.id()
assert props.styles[0].name == "Name"
assert props.styles[0].total_elements == 0
class TestIsEditingStyles(NewFile):
def test_run(self):
bpy.context.scene.BIMStylesProperties.is_editing = False
subject.is_editing_styles() is False
bpy.context.scene.BIMStylesProperties.is_editing = True
subject.is_editing_styles() is True
class TestGetRepresentationStyleItem(NewFile):
def test_run(self):
tool.Ifc.set(ifc := ifcopenshell.file())
builder = ShapeBuilder(ifc)
rectangle = builder.rectangle()
assert subject.get_representation_item_style(rectangle) == None
style = ifc.createIfcSurfaceStyle()
subject.assign_style_to_representation_item(rectangle, style)
assert subject.get_representation_item_style(rectangle) == style
class TestAssignStyleToRepresentationItem(NewFile):
def test_run(self):
tool.Ifc.set(ifc := ifcopenshell.file())
builder = ShapeBuilder(ifc)
rectangle = builder.rectangle()
# assigning new style
style1 = ifc.createIfcSurfaceStyle()
subject.assign_style_to_representation_item(rectangle, style1)
assert subject.get_representation_item_style(rectangle) == style1
# changing style
style2 = ifc.createIfcSurfaceStyle()
subject.assign_style_to_representation_item(rectangle, style2)
assert subject.get_representation_item_style(rectangle) == style2
# unassigning styles
subject.assign_style_to_representation_item(rectangle, None)
assert subject.get_representation_item_style(rectangle) == None
+58
View File
@@ -0,0 +1,58 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import numpy as np
import ifcopenshell
import ifcopenshell.api
import test.bim.bootstrap
import blenderbim.core.tool
import blenderbim.tool as tool
from blenderbim.tool import Surveyor as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Surveyor)
class TestGetGlobalMatrix(test.bim.bootstrap.NewFile):
def test_getting_an_absolute_matrix_if_no_blender_offset(self):
props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = False
obj = bpy.data.objects.new("Object", None)
assert (subject.get_absolute_matrix(obj) == np.array(obj.matrix_world)).all()
def test_applying_an_object_placement_blender_offset(self):
ifc = ifcopenshell.file()
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
unit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT", prefix="MILLI")
ifcopenshell.api.run("unit.assign_unit", ifc, units=[unit])
tool.Ifc.set(ifc)
props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = True
props.blender_offset_x = "1000"
props.blender_offset_y = "2000"
props.blender_offset_z = "3000"
props.blender_x_axis_abscissa = "0"
props.blender_x_axis_ordinate = "1"
obj = bpy.data.objects.new("Object", None)
obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT"
matrix = ifcopenshell.util.geolocation.local2global(np.array(obj.matrix_world), 1.0, 2.0, 3.0, 0.0, 1.0)
assert (subject.get_absolute_matrix(obj) == matrix).all()
+305
View File
@@ -0,0 +1,305 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.api
import blenderbim.core.tool
import blenderbim.tool as tool
import numpy as np
from test.bim.bootstrap import NewFile
from blenderbim.tool.system import System as subject
from mathutils import Euler, Vector, Matrix
from math import pi
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.System)
class TestAddPorts(NewFile):
def setup_mep_segment(self):
bpy.ops.bim.create_project()
bpy.ops.mesh.primitive_cube_add(size=1, location=(0, 0, 0))
obj = bpy.data.objects["Cube"]
obj.scale = (1, 1, 5)
bpy.ops.bim.assign_class(ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT", userdefined_type="")
element = tool.Ifc.get_entity(obj)
obj.matrix_world = Euler((pi / 2, 0, pi / 2)).to_matrix().to_4x4() @ obj.matrix_world
# move origin
for v in obj.data.vertices:
v.co += Vector((0, 0, 2.5))
return obj, element
def check_ports_matrices(self, ports, expected_matrices):
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
for port, expected_matrix in zip(ports, expected_matrices, strict=True):
port_matrix = tool.Model.get_element_matrix(port)
port_matrix.translation *= si_conversion
assert np.allclose(port_matrix, expected_matrix, atol=1.0e-5)
def test_run(self):
# default use
obj, element = self.setup_mep_segment()
ports = subject.add_ports(obj)
assert len(subject.get_ports(element)) == len(ports)
self.check_ports_matrices(ports, (obj.matrix_world, obj.matrix_world @ Matrix.Translation((0, 0, 5))))
# skip end port
obj, element = self.setup_mep_segment()
ports = subject.add_ports(obj, add_end_port=False)
self.check_ports_matrices(ports, (obj.matrix_world,))
# skip start port
obj, element = self.setup_mep_segment()
ports = subject.add_ports(obj, add_start_port=False)
self.check_ports_matrices(ports, (obj.matrix_world @ Matrix.Translation((0, 0, 5)),))
# position end port
obj, element = self.setup_mep_segment()
ports = subject.add_ports(obj, end_port_pos=Vector((1, 2, 3)))
translated_matrix = obj.matrix_world.copy()
translated_matrix.translation = (1, 2, 3)
self.check_ports_matrices(ports, (obj.matrix_world, translated_matrix))
# offset end port
obj, element = self.setup_mep_segment()
ports = subject.add_ports(obj, offset_end_port=Vector((0, 0, 1)))
translated_matrix = obj.matrix_world.copy()
translated_matrix.translation = (5, 0, 1)
self.check_ports_matrices(ports, (obj.matrix_world, translated_matrix))
class TestCreateEmptyAtCursorWithElementOrientation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
obj = bpy.data.objects.new("Object", None)
element = ifc.createIfcWall()
tool.Ifc.link(element, obj)
obj = subject.create_empty_at_cursor_with_element_orientation(element)
assert obj.matrix_world == bpy.context.scene.cursor.matrix
class TestDeleteElementObjects(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
obj = bpy.data.objects.new("Object", None)
element = ifc.createIfcWall()
tool.Ifc.link(element, obj)
subject.delete_element_objects([element])
assert not bpy.data.objects.get("Object")
class TestDisableEditingSystem(NewFile):
def test_run(self):
bpy.context.scene.BIMSystemProperties.edited_system_id = 10
subject.disable_editing_system()
assert bpy.context.scene.BIMSystemProperties.edited_system_id == 0
class TestDisableSystemEditingUI(NewFile):
def test_run(self):
subject.enable_system_editing_ui()
subject.disable_system_editing_ui()
assert bpy.context.scene.BIMSystemProperties.is_editing is False
class TestEnableSystemEditingUI(NewFile):
def test_run(self):
subject.enable_system_editing_ui()
assert bpy.context.scene.BIMSystemProperties.is_editing is True
class TestExportSystemAttributes(NewFile):
def test_run(self):
TestImportSystemAttributes().test_importing_a_system()
assert subject.export_system_attributes() == {
"GlobalId": "GlobalId",
"Name": "Name",
"Description": "Description",
"ObjectType": "ObjectType",
}
class TestGetConnectedPort(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
port1 = ifcopenshell.api.run("system.add_port", ifc)
port2 = ifcopenshell.api.run("system.add_port", ifc)
ifcopenshell.api.run("system.connect_port", ifc, port1=port1, port2=port2)
assert subject.get_connected_port(port1) == port2
assert subject.get_connected_port(port2) == port1
class TestGetPorts(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
element = ifc.createIfcDuctSegment()
port = ifc.createIfcDistributionPort()
ifcopenshell.api.run("system.assign_port", ifc, element=element, port=port)
subject.get_ports(element) == [port]
class TestImportSystemAttributes(NewFile):
def test_importing_a_system(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
system = ifc.createIfcSystem()
system.GlobalId = "GlobalId"
system.Name = "Name"
system.Description = "Description"
system.ObjectType = "ObjectType"
subject().import_system_attributes(system)
props = bpy.context.scene.BIMSystemProperties
assert props.system_attributes.get("GlobalId").string_value == "GlobalId"
assert props.system_attributes.get("Name").string_value == "Name"
assert props.system_attributes.get("Description").string_value == "Description"
assert props.system_attributes.get("ObjectType").string_value == "ObjectType"
def test_importing_a_building_system(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
system = ifc.createIfcBuildingSystem()
system.GlobalId = "GlobalId"
system.Name = "Name"
system.Description = "Description"
system.ObjectType = "ObjectType"
system.PredefinedType = "SHADING"
system.LongName = "LongName"
subject().import_system_attributes(system)
props = bpy.context.scene.BIMSystemProperties
assert props.system_attributes.get("GlobalId").string_value == "GlobalId"
assert props.system_attributes.get("Name").string_value == "Name"
assert props.system_attributes.get("Description").string_value == "Description"
assert props.system_attributes.get("ObjectType").string_value == "ObjectType"
assert props.system_attributes.get("PredefinedType").enum_value == "SHADING"
assert props.system_attributes.get("LongName").string_value == "LongName"
def test_importing_a_distribution_system(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
system = ifc.createIfcDistributionSystem()
system.GlobalId = "GlobalId"
system.Name = "Name"
system.Description = "Description"
system.ObjectType = "ObjectType"
system.PredefinedType = "ELECTRICAL"
system.LongName = "LongName"
subject().import_system_attributes(system)
props = bpy.context.scene.BIMSystemProperties
assert props.system_attributes.get("GlobalId").string_value == "GlobalId"
assert props.system_attributes.get("Name").string_value == "Name"
assert props.system_attributes.get("Description").string_value == "Description"
assert props.system_attributes.get("ObjectType").string_value == "ObjectType"
assert props.system_attributes.get("PredefinedType").enum_value == "ELECTRICAL"
assert props.system_attributes.get("LongName").string_value == "LongName"
class TestImportSystems(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
system = ifc.createIfcDistributionSystem()
zone = ifc.createIfcZone()
subject.import_systems()
props = bpy.context.scene.BIMSystemProperties
assert len(props.systems) == 2
assert props.systems[0].ifc_definition_id == system.id()
assert props.systems[0].name == "Unnamed"
assert props.systems[0].ifc_class == "IfcDistributionSystem"
class TestLoadPorts(NewFile):
def test_run(self):
bpy.ops.bim.create_project()
ifc = tool.Ifc.get()
element = ifc.create_entity("IfcChiller")
obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(element, obj)
port = ifc.create_entity("IfcDistributionPort")
subject.load_ports(element, [port])
obj = tool.Ifc.get_object(port)
assert obj
assert obj.users_collection
assert list(obj.location) == [0, 0, 0]
class TestRunGeometryEditObjectPlacement(NewFile):
def test_nothing(self):
pass
class TestRunRootAssignClass(NewFile):
def test_nothing(self):
pass
class TestSelectSystemProducts(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcPump")
system = ifcopenshell.api.run("system.add_system", ifc, ifc_class="IfcSystem")
ifcopenshell.api.run("system.assign_system", ifc, products=[element], system=system)
obj = bpy.data.objects.new("Object", None)
bpy.context.scene.collection.objects.link(obj)
tool.Ifc.link(element, obj)
subject.select_system_products(system)
assert obj in bpy.context.selected_objects
class TestSetActiveSystem(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
system = ifcopenshell.api.run("system.add_system", ifc, ifc_class="IfcSystem")
subject.set_active_edited_system(system)
assert bpy.context.scene.BIMSystemProperties.edited_system_id == system.id()
class TestFlowElementAndControls(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
flow_element = ifc.createIfcFlowSegment()
flow_control = ifc.createIfcController()
flow_control1 = ifc.createIfcController()
assert len(subject.get_flow_element_controls(flow_element)) == 0
assert subject.get_flow_control_flow_element(flow_control) == None
ifcopenshell.api.run(
"system.assign_flow_control",
ifc,
related_flow_control=flow_control,
relating_flow_element=flow_element,
)
ifcopenshell.api.run(
"system.assign_flow_control",
ifc,
related_flow_control=flow_control1,
relating_flow_element=flow_element,
)
controls = subject.get_flow_element_controls(flow_element)
assert set(controls) == set((flow_control, flow_control1))
assert subject.get_flow_control_flow_element(flow_control) == flow_element
+178
View File
@@ -0,0 +1,178 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. 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.type import Type as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Type)
class TestChangeObjectData(NewFile):
def test_change_single_object_data(self):
data1 = bpy.data.meshes.new("Mesh")
data2 = bpy.data.meshes.new("Mesh")
obj1 = bpy.data.objects.new("Object", data1)
obj2 = bpy.data.objects.new("Object", data1)
subject.change_object_data(obj1, data2, is_global=False)
assert obj1.data == data2
assert obj2.data == data1
def test_change_object_data_globally(self):
data1 = bpy.data.meshes.new("Mesh")
data2 = bpy.data.meshes.new("Mesh")
obj1 = bpy.data.objects.new("Object", data1)
obj2 = bpy.data.objects.new("Object", data1)
subject.change_object_data(obj1, data2, is_global=True)
assert obj1.data == data2
assert obj2.data == data2
class TestDisableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
obj.BIMTypeProperties.is_editing_type = True
subject.disable_editing(obj)
assert obj.BIMTypeProperties.is_editing_type is False
class TestGetBodyContext(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
context = ifc.createIfcGeometricRepresentationSubContext(
ContextType="Model", ContextIdentifier="Body", TargetView="MODEL_VIEW"
)
tool.Ifc.set(ifc)
assert subject.get_body_context() == context
class TestGetBodyRepresentation(NewFile):
def test_get_product_representation(self):
ifc = ifcopenshell.file()
rep = ifc.createIfcShapeRepresentation(ContextOfItems=ifc.createIfcGeometricRepresentationSubContext())
body_rep = ifc.createIfcShapeRepresentation(
ContextOfItems=ifc.createIfcGeometricRepresentationSubContext(ContextIdentifier="Body")
)
element = ifc.createIfcWall(Representation=ifc.createIfcProductRepresentation(Representations=[rep, body_rep]))
assert subject.get_body_representation(element) == body_rep
def test_get_type_product_representation(self):
ifc = ifcopenshell.file()
rep = ifc.createIfcShapeRepresentation(ContextOfItems=ifc.createIfcGeometricRepresentationSubContext())
body_rep = ifc.createIfcShapeRepresentation(
ContextOfItems=ifc.createIfcGeometricRepresentationSubContext(ContextIdentifier="Body")
)
element = ifc.createIfcWallType(
RepresentationMaps=[
ifc.createIfcRepresentationMap(MappedRepresentation=rep),
ifc.createIfcRepresentationMap(MappedRepresentation=body_rep),
]
)
assert subject.get_body_representation(element) == body_rep
class TestGetIfcRepresentationClass(NewFile):
def test_detecting_profile_set_representations(self):
ifc = ifcopenshell.file()
element = ifc.createIfcColumn()
ifc.createIfcRelAssociatesMaterial(
RelatingMaterial=ifc.createIfcMaterialProfileSetUsage(), RelatedObjects=[element]
)
assert subject.get_ifc_representation_class(element) == "IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage"
def test_detecting_layer_set_representations(self):
ifc = ifcopenshell.file()
element = ifc.createIfcColumn()
ifc.createIfcRelAssociatesMaterial(
RelatingMaterial=ifc.createIfcMaterialLayerSetUsage(), RelatedObjects=[element]
)
assert subject.get_ifc_representation_class(element) == "IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids"
def test_returning_null_for_non_parametric_representations(self):
ifc = ifcopenshell.file()
assert subject.get_ifc_representation_class(ifc.createIfcColumn()) is None
class TestGetModelTypes(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
type_classes = ("IfcWallType", "IfcDoorStyle", "IfcWindowStyle", "IfcTypeProduct")
types = [ifcopenshell.api.run("root.create_entity", ifc, ifc_class=c) for c in type_classes]
assert set(subject.get_model_types()) == set(types)
class TestGetObjectData(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 TestGetProfileSetUsage(NewFile):
def test_getting_a_profile_set_usage(self):
ifc = ifcopenshell.file()
element = ifc.createIfcColumn()
assert subject.get_profile_set_usage(element) is None
usage = ifc.createIfcMaterialProfileSetUsage()
ifc.createIfcRelAssociatesMaterial(RelatingMaterial=usage, RelatedObjects=[element])
assert subject.get_profile_set_usage(element) == usage
class TestGetRepresentationContext(NewFile):
def test_getting_a_profile_set_usage(self):
ifc = ifcopenshell.file()
context = ifc.createIfcGeometricRepresentationSubContext()
representation = ifc.createIfcShapeRepresentation(ContextOfItems=context)
assert subject.get_representation_context(representation) == context
class TestGetTypeOccurrences(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
wall = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
ifcopenshell.api.run("type.assign_type", ifc, related_objects=[wall], relating_type=wall_type)
assert subject.get_type_occurrences(wall_type) == (wall,)
class TestHasMaterialUsage(NewFile):
def test_getting_a_profile_set_usage(self):
ifc = ifcopenshell.file()
element = ifc.createIfcColumn()
assert subject.has_material_usage(element) is False
usage = ifc.createIfcMaterialProfileSetUsage()
ifc.createIfcRelAssociatesMaterial(RelatingMaterial=usage, RelatedObjects=[element])
assert subject.has_material_usage(element) is True
class TestRunGeometryAddRepresentation(NewFile):
def test_nothing(self):
pass
class TestRunGeometrySwitchRepresentation(NewFile):
def test_nothing(self):
pass
+313
View File
@@ -0,0 +1,313 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai 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.
#
# Bonsai 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 Bonsai. 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.unit import Unit as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Unit)
class TestClearActiveUnit(NewFile):
def test_run(self):
bpy.context.scene.BIMUnitProperties.active_unit_id = 1
subject.clear_active_unit()
assert bpy.context.scene.BIMUnitProperties.active_unit_id == 0
class TestDisableEditingUnits(NewFile):
def test_run(self):
bpy.context.scene.BIMUnitProperties.is_editing = True
subject.disable_editing_units()
assert bpy.context.scene.BIMUnitProperties.is_editing == False
class TestEnableEditingUnits(NewFile):
def test_run(self):
bpy.context.scene.BIMUnitProperties.is_editing = False
subject.enable_editing_units()
assert bpy.context.scene.BIMUnitProperties.is_editing == True
class TestExportUnitAttributes(NewFile):
def test_exporting_derived_units(self):
TestImportUnitAttributes().test_importing_derived_units()
assert subject.export_unit_attributes() == {
"UnitType": "ANGULARVELOCITYUNIT",
"UserDefinedType": "UserDefinedType",
}
def test_exporting_monetary_units(self):
TestImportUnitAttributes().test_importing_monetary_units()
assert subject.export_unit_attributes() == {"Currency": "Currency"}
def test_exporting_monetary_units_ifc2x3(self):
TestImportUnitAttributes().test_importing_monetary_units_ifc2x3()
assert subject.export_unit_attributes() == {"Currency": "USD"}
def test_exporting_context_dependent_units(self):
TestImportUnitAttributes().test_importing_context_dependent_units()
assert subject.export_unit_attributes() == {
"UnitType": "ABSORBEDDOSEUNIT",
"Name": "Name",
"Dimensions": [1, 2, 3, 4, 5, 6, 7],
}
def test_exporting_conversion_based_units(self):
TestImportUnitAttributes().test_importing_conversion_based_units()
assert subject.export_unit_attributes() == {
"UnitType": "ABSORBEDDOSEUNIT",
"Name": "Name",
"Dimensions": [1, 2, 3, 4, 5, 6, 7],
}
def test_exporting_conversion_based_with_offset_units(self):
TestImportUnitAttributes().test_importing_conversion_based_with_offset_units()
assert subject.export_unit_attributes() == {
"UnitType": "ABSORBEDDOSEUNIT",
"Name": "Name",
"Dimensions": [1, 2, 3, 4, 5, 6, 7],
"ConversionOffset": 1,
}
def test_exporting_si_units(self):
TestImportUnitAttributes().test_importing_si_units()
assert subject.export_unit_attributes() == {"UnitType": "ABSORBEDDOSEUNIT", "Prefix": "EXA", "Name": "AMPERE"}
class TestGetSceneUnitName(NewFile):
def test_getting_an_imperial_name(self):
bpy.context.scene.unit_settings.system = "IMPERIAL"
bpy.context.scene.unit_settings.length_unit = "MILES"
bpy.context.scene.BIMProperties.area_unit = "square foot"
bpy.context.scene.BIMProperties.volume_unit = "cubic inch"
assert subject.get_scene_unit_name("LENGTHUNIT") == "mile"
assert subject.get_scene_unit_name("AREAUNIT") == "square foot"
assert subject.get_scene_unit_name("VOLUMEUNIT") == "cubic inch"
bpy.context.scene.unit_settings.length_unit = "FEET"
assert subject.get_scene_unit_name("LENGTHUNIT") == "foot"
assert subject.get_scene_unit_name("AREAUNIT") == "square foot"
assert subject.get_scene_unit_name("VOLUMEUNIT") == "cubic inch"
bpy.context.scene.unit_settings.length_unit = "INCHES"
assert subject.get_scene_unit_name("LENGTHUNIT") == "inch"
assert subject.get_scene_unit_name("AREAUNIT") == "square foot"
assert subject.get_scene_unit_name("VOLUMEUNIT") == "cubic inch"
bpy.context.scene.unit_settings.length_unit = "THOU"
assert subject.get_scene_unit_name("LENGTHUNIT") == "thou"
assert subject.get_scene_unit_name("AREAUNIT") == "square foot"
assert subject.get_scene_unit_name("VOLUMEUNIT") == "cubic inch"
bpy.context.scene.unit_settings.length_unit = "ADAPTIVE"
assert subject.get_scene_unit_name("LENGTHUNIT") == "foot"
assert subject.get_scene_unit_name("AREAUNIT") == "square foot"
assert subject.get_scene_unit_name("VOLUMEUNIT") == "cubic inch"
def test_getting_a_name_with_no_unit_system(self):
bpy.context.scene.unit_settings.system = "NONE"
assert subject.get_scene_unit_name("LENGTHUNIT") == "foot"
class TestGetSceneUnitSIPrefix:
def test_run(self):
bpy.context.scene.unit_settings.system = "METRIC"
bpy.context.scene.unit_settings.length_unit = "METERS"
assert subject.get_scene_unit_si_prefix("LENGTHUNIT") is None
bpy.context.scene.unit_settings.length_unit = "MICROMETERS"
assert subject.get_scene_unit_si_prefix("LENGTHUNIT") == "MICRO"
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
assert subject.get_scene_unit_si_prefix("LENGTHUNIT") == "MILLI"
bpy.context.scene.unit_settings.length_unit = "CENTIMETERS"
assert subject.get_scene_unit_si_prefix("LENGTHUNIT") == "CENTI"
bpy.context.scene.unit_settings.length_unit = "KILOMETERS"
assert subject.get_scene_unit_si_prefix("LENGTHUNIT") == "KILO"
bpy.context.scene.unit_settings.length_unit = "ADAPTIVE"
assert subject.get_scene_unit_si_prefix("LENGTHUNIT") is None
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
assert subject.get_scene_unit_si_prefix("AREAUNIT") is None
bpy.context.scene.BIMProperties.area_unit = "MILLI/SQUARE_METRE"
assert subject.get_scene_unit_si_prefix("AREAUNIT") == "MILLI"
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
assert subject.get_scene_unit_si_prefix("VOLUMEUNIT") is None
bpy.context.scene.BIMProperties.volume_unit = "MILLI/CUBIC_METRE"
assert subject.get_scene_unit_si_prefix("VOLUMEUNIT") == "MILLI"
class TestImportUnitAttributes(NewFile):
def test_importing_derived_units(self):
tool.Ifc.set(ifc := ifcopenshell.file())
unit = ifc.createIfcDerivedUnit()
unit.UnitType = "ANGULARVELOCITYUNIT"
unit.UserDefinedType = "UserDefinedType"
subject.import_unit_attributes(unit)
props = bpy.context.scene.BIMUnitProperties
assert props.unit_attributes.get("UnitType").enum_value == "ANGULARVELOCITYUNIT"
assert props.unit_attributes.get("UserDefinedType").string_value == "UserDefinedType"
def test_importing_monetary_units(self):
tool.Ifc.set(ifc := ifcopenshell.file())
unit = ifc.createIfcMonetaryUnit()
unit.Currency = "Currency"
subject.import_unit_attributes(unit)
props = bpy.context.scene.BIMUnitProperties
assert props.unit_attributes.get("Currency").string_value == "Currency"
def test_importing_monetary_units_ifc2x3(self):
tool.Ifc.set(ifc := ifcopenshell.file(schema="IFC2X3"))
unit = ifc.createIfcMonetaryUnit()
unit.Currency = "USD"
subject.import_unit_attributes(unit)
props = bpy.context.scene.BIMUnitProperties
assert props.unit_attributes.get("Currency").enum_value == "USD"
def test_importing_context_dependent_units(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
unit = ifc.createIfcContextDependentUnit()
unit.UnitType = "ABSORBEDDOSEUNIT"
unit.Name = "Name"
unit.Dimensions = ifc.createIfcDimensionalExponents(1, 2, 3, 4, 5, 6, 7)
subject.import_unit_attributes(unit)
props = bpy.context.scene.BIMUnitProperties
assert props.unit_attributes.get("UnitType").enum_value == "ABSORBEDDOSEUNIT"
assert props.unit_attributes.get("Name").string_value == "Name"
assert props.unit_attributes.get("Dimensions").string_value == "[1, 2, 3, 4, 5, 6, 7]"
def test_importing_conversion_based_units(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
unit = ifc.createIfcConversionBasedUnit()
unit.UnitType = "ABSORBEDDOSEUNIT"
unit.Name = "Name"
unit.Dimensions = ifc.createIfcDimensionalExponents(1, 2, 3, 4, 5, 6, 7)
subject.import_unit_attributes(unit)
props = bpy.context.scene.BIMUnitProperties
assert props.unit_attributes.get("UnitType").enum_value == "ABSORBEDDOSEUNIT"
assert props.unit_attributes.get("Name").string_value == "Name"
assert props.unit_attributes.get("Dimensions").string_value == "[1, 2, 3, 4, 5, 6, 7]"
def test_importing_conversion_based_with_offset_units(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
unit = ifc.createIfcConversionBasedUnitWithOffset()
unit.UnitType = "ABSORBEDDOSEUNIT"
unit.Name = "Name"
unit.Dimensions = ifc.createIfcDimensionalExponents(1, 2, 3, 4, 5, 6, 7)
unit.ConversionOffset = 1
subject.import_unit_attributes(unit)
props = bpy.context.scene.BIMUnitProperties
assert props.unit_attributes.get("UnitType").enum_value == "ABSORBEDDOSEUNIT"
assert props.unit_attributes.get("Name").string_value == "Name"
assert props.unit_attributes.get("Dimensions").string_value == "[1, 2, 3, 4, 5, 6, 7]"
assert props.unit_attributes.get("ConversionOffset").float_value == 1
def test_importing_si_units(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
unit = ifc.createIfcSIUnit()
unit.UnitType = "ABSORBEDDOSEUNIT"
unit.Prefix = "EXA"
unit.Name = "AMPERE"
subject.import_unit_attributes(unit)
props = bpy.context.scene.BIMUnitProperties
assert props.unit_attributes.get("UnitType").enum_value == "ABSORBEDDOSEUNIT"
assert props.unit_attributes.get("Prefix").enum_value == "EXA"
assert props.unit_attributes.get("Name").enum_value == "AMPERE"
assert props.unit_attributes.get("Dimensions") is None
class TestImportUnits(NewFile):
def test_importing_multiple_units(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
unit1 = ifc.createIfcDerivedUnit(UnitType="ANGULARVELOCITYUNIT")
unit2 = ifc.createIfcMonetaryUnit(Currency="Currency")
unit3 = ifc.createIfcContextDependentUnit(Name="Name", UnitType="ABSORBEDDOSEUNIT")
unit4 = ifc.createIfcConversionBasedUnit(Name="Name", UnitType="ABSORBEDDOSEUNIT")
unit5 = ifc.createIfcSIUnit(Name="AMPERE", Prefix="MILLI", UnitType="ABSORBEDDOSEUNIT")
unit6 = ifc.createIfcSIUnit(Name="CUBIC_METRE", Prefix="CENTI", UnitType="ABSORBEDDOSEUNIT")
ifc.createIfcUnitAssignment(Units=[unit2])
subject.import_units()
props = bpy.context.scene.BIMUnitProperties
assert len(props.units) == 6
assert props.units[0].ifc_definition_id == unit1.id()
assert props.units[0].name == ""
assert props.units[0].is_assigned is False
assert props.units[0].unit_type == unit1.UnitType
assert props.units[0].ifc_class == unit1.is_a()
assert props.units[1].ifc_definition_id == unit2.id()
assert props.units[1].name == "Currency"
assert props.units[1].is_assigned is True
assert props.units[1].unit_type == "CURRENCY"
assert props.units[1].ifc_class == unit2.is_a()
assert props.units[2].ifc_definition_id == unit3.id()
assert props.units[2].name == "Name"
assert props.units[2].is_assigned is False
assert props.units[2].unit_type == unit3.UnitType
assert props.units[2].ifc_class == unit3.is_a()
assert props.units[3].ifc_definition_id == unit4.id()
assert props.units[3].name == "Name"
assert props.units[3].is_assigned is False
assert props.units[3].unit_type == unit4.UnitType
assert props.units[3].ifc_class == unit4.is_a()
assert props.units[4].ifc_definition_id == unit5.id()
assert props.units[4].name == "MILLIAMPERE"
assert props.units[4].is_assigned is False
assert props.units[4].unit_type == unit5.UnitType
assert props.units[4].ifc_class == unit5.is_a()
assert props.units[5].ifc_definition_id == unit6.id()
assert props.units[5].name == "CUBIC CENTIMETRE"
assert props.units[5].is_assigned is False
assert props.units[5].unit_type == unit6.UnitType
assert props.units[5].ifc_class == unit6.is_a()
class TestIsSceneUnitMetric(NewFile):
def test_run(self):
props = bpy.context.scene.unit_settings
props.system = "METRIC"
assert subject.is_scene_unit_metric() is True
props.system = "IMPERIAL"
assert subject.is_scene_unit_metric() is False
props.system = "NONE"
assert subject.is_scene_unit_metric() is True
class TestIsUnitClass:
def test_run(self):
ifc = ifcopenshell.file()
assert subject.is_unit_class(ifc.createIfcSIUnit(), "IfcNamedUnit") is True
assert subject.is_unit_class(ifc.createIfcSIUnit(), "IfcMonetaryUnit") is False
class TestSetActiveUnit(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
unit = ifc.createIfcSIUnit()
subject.set_active_unit(unit)
assert bpy.context.scene.BIMUnitProperties.active_unit_id == unit.id()