mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-29 00:03:17 +00:00
Rename source dir
This commit is contained in:
@@ -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/>.
|
||||
@@ -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
|
||||
@@ -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"})
|
||||
@@ -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
|
||||
@@ -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])
|
||||
@@ -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")
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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")]
|
||||
@@ -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
|
||||
|
||||
"""
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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]]
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"]]
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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"
|
||||
@@ -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]
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user