mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-28 11:18:18 +00:00
New feature to resize / squash / stretch objects to their storey height. Useful for remodeling IFCs for construction simulations.
This commit is contained in:
@@ -20,6 +20,7 @@ import bpy
|
|||||||
from . import ui, prop, operator
|
from . import ui, prop, operator
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
|
operator.ResizeToStorey,
|
||||||
operator.SetOverrideColour,
|
operator.SetOverrideColour,
|
||||||
operator.SetViewportShadowFromSun,
|
operator.SetViewportShadowFromSun,
|
||||||
operator.SnapSpacesTogether,
|
operator.SnapSpacesTogether,
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import blenderbim.bim.handler
|
import blenderbim.bim.handler
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
import blenderbim.core.misc as core
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from mathutils import Vector, Matrix, Euler
|
from mathutils import Vector, Matrix, Euler
|
||||||
|
|
||||||
@@ -109,3 +111,17 @@ class SnapSpacesTogether(bpy.types.Operator):
|
|||||||
for v in polygon.vertices:
|
for v in polygon.vertices:
|
||||||
obj.data.vertices[v].co += offset
|
obj.data.vertices[v].co += offset
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class ResizeToStorey(bpy.types.Operator, Operator):
|
||||||
|
bl_idname = "bim.resize_to_storey"
|
||||||
|
bl_label = "Resize To Storey"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.selected_objects
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
for obj in context.selected_objects:
|
||||||
|
core.resize_to_storey(tool.Misc, obj=obj)
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ class BIM_PT_misc_utilities(bpy.types.Panel):
|
|||||||
row = layout.split(factor=0.2, align=True)
|
row = layout.split(factor=0.2, align=True)
|
||||||
row.prop(props, "override_colour", text="")
|
row.prop(props, "override_colour", text="")
|
||||||
row.operator("bim.set_override_colour")
|
row.operator("bim.set_override_colour")
|
||||||
row = layout.row(align=True)
|
row = layout.row()
|
||||||
row.operator("bim.set_viewport_shadow_from_sun")
|
row.operator("bim.set_viewport_shadow_from_sun")
|
||||||
row = layout.row(align=True)
|
row = layout.row()
|
||||||
row.operator("bim.snap_spaces_together")
|
row.operator("bim.snap_spaces_together")
|
||||||
|
row = layout.row()
|
||||||
|
row.operator("bim.resize_to_storey")
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
def resize_to_storey(misc, obj=None):
|
||||||
|
storey = misc.get_object_storey(obj)
|
||||||
|
if not storey:
|
||||||
|
return
|
||||||
|
height = misc.get_storey_height_in_si(storey)
|
||||||
|
if not height:
|
||||||
|
return
|
||||||
|
misc.set_object_origin_to_bottom(obj)
|
||||||
|
misc.move_object_to_elevation(obj, misc.get_storey_elevation_in_si(storey))
|
||||||
|
misc.scale_object_to_height(obj, height)
|
||||||
|
misc.mark_object_as_edited(obj)
|
||||||
@@ -77,6 +77,17 @@ class Material:
|
|||||||
def unlink(cls, obj): pass
|
def unlink(cls, obj): pass
|
||||||
|
|
||||||
|
|
||||||
|
@interface
|
||||||
|
class Misc:
|
||||||
|
def get_object_storey(cls, obj): pass
|
||||||
|
def get_storey_elevation_in_si(cls, storey): pass
|
||||||
|
def get_storey_height_in_si(cls, storey): pass
|
||||||
|
def mark_object_as_edited(cls, obj): pass
|
||||||
|
def move_object_to_elevation(cls, obj, elevation): pass
|
||||||
|
def scale_object_to_height(cls, obj, height): pass
|
||||||
|
def set_object_origin_to_bottom(cls, obj): pass
|
||||||
|
|
||||||
|
|
||||||
@interface
|
@interface
|
||||||
class Owner:
|
class Owner:
|
||||||
def add_address_attribute(cls, name): pass
|
def add_address_attribute(cls, name): pass
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from blenderbim.tool.container import Container
|
|||||||
from blenderbim.tool.context import Context
|
from blenderbim.tool.context import Context
|
||||||
from blenderbim.tool.ifc import Ifc
|
from blenderbim.tool.ifc import Ifc
|
||||||
from blenderbim.tool.material import Material
|
from blenderbim.tool.material import Material
|
||||||
|
from blenderbim.tool.misc import Misc
|
||||||
from blenderbim.tool.owner import Owner
|
from blenderbim.tool.owner import Owner
|
||||||
from blenderbim.tool.style import Style
|
from blenderbim.tool.style import Style
|
||||||
from blenderbim.tool.surveyor import Surveyor
|
from blenderbim.tool.surveyor import Surveyor
|
||||||
|
|||||||
@@ -60,9 +60,7 @@ class Container(blenderbim.core.tool.Container):
|
|||||||
for rel in parent.IsDecomposedBy or []:
|
for rel in parent.IsDecomposedBy or []:
|
||||||
related_objects = []
|
related_objects = []
|
||||||
for element in rel.RelatedObjects:
|
for element in rel.RelatedObjects:
|
||||||
related_objects.append(
|
related_objects.append((element, ifcopenshell.util.placement.get_storey_elevation(element)))
|
||||||
(element, ifcopenshell.util.placement.get_storey_elevation(element))
|
|
||||||
)
|
|
||||||
related_objects = sorted(related_objects, key=lambda e: e[1])
|
related_objects = sorted(related_objects, key=lambda e: e[1])
|
||||||
for element in related_objects:
|
for element in related_objects:
|
||||||
element = element[0]
|
element = element[0]
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import ifcopenshell
|
||||||
|
import blenderbim.core.tool
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
from mathutils import Vector, Matrix
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class Misc(blenderbim.core.tool.Misc):
|
||||||
|
@classmethod
|
||||||
|
def get_object_storey(cls, obj):
|
||||||
|
storey = ifcopenshell.util.element.get_container(tool.Ifc.get_entity(obj))
|
||||||
|
if storey and storey.is_a("IfcBuildingStorey"):
|
||||||
|
return storey
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_storey_elevation_in_si(cls, storey):
|
||||||
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
|
elevation = ifcopenshell.util.placement.get_storey_elevation(storey)
|
||||||
|
return elevation * unit_scale
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_storey_height_in_si(cls, storey):
|
||||||
|
building = ifcopenshell.util.element.get_aggregate(storey)
|
||||||
|
related_objects = []
|
||||||
|
for rel in building.IsDecomposedBy:
|
||||||
|
if rel.is_a("IfcRelAggregates"):
|
||||||
|
for element in rel.RelatedObjects:
|
||||||
|
if element.is_a("IfcBuildingStorey"):
|
||||||
|
related_objects.append((element, ifcopenshell.util.placement.get_storey_elevation(element)))
|
||||||
|
related_objects = sorted(related_objects, key=lambda e: e[1])
|
||||||
|
storey_elevation = None
|
||||||
|
next_storey_elevation = None
|
||||||
|
for related_object in related_objects:
|
||||||
|
if related_object[0] == storey:
|
||||||
|
storey_elevation = related_object[1]
|
||||||
|
elif storey_elevation is not None:
|
||||||
|
next_storey_elevation = related_object[1]
|
||||||
|
break
|
||||||
|
if next_storey_elevation:
|
||||||
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
|
return (next_storey_elevation - storey_elevation) * unit_scale
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_object_origin_to_bottom(cls, obj):
|
||||||
|
absolute_bound_box = [obj.matrix_world @ Vector(c) for c in obj.bound_box]
|
||||||
|
min_z = min([c[2] for c in absolute_bound_box])
|
||||||
|
new_origin = obj.matrix_world.translation.copy()
|
||||||
|
new_origin[2] = min_z
|
||||||
|
obj.data.transform(
|
||||||
|
Matrix.Translation(
|
||||||
|
(obj.matrix_world.inverted().to_quaternion() @ (obj.matrix_world.translation - new_origin))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
obj.matrix_world.translation = new_origin
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def move_object_to_elevation(cls, obj, elevation):
|
||||||
|
obj.matrix_world.translation[2] = elevation
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def scale_object_to_height(cls, obj, height):
|
||||||
|
absolute_bound_box = [obj.matrix_world @ Vector(c) for c in obj.bound_box]
|
||||||
|
max_z = max([c[2] for c in absolute_bound_box])
|
||||||
|
min_z = min([c[2] for c in absolute_bound_box])
|
||||||
|
current_absolute_height = max_z - min_z
|
||||||
|
scale_factor = height / current_absolute_height
|
||||||
|
obj.matrix_world @= Matrix.Scale(scale_factor, 4, obj.matrix_world.to_quaternion() @ Vector((0, 0, 1)))
|
||||||
|
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def mark_object_as_edited(cls, obj):
|
||||||
|
IfcStore.edited_objs.add(obj)
|
||||||
@@ -2,24 +2,36 @@
|
|||||||
Feature: Misc
|
Feature: Misc
|
||||||
|
|
||||||
Scenario: Set override colour
|
Scenario: Set override colour
|
||||||
Given an empty IFC project
|
Given an empty Blender session
|
||||||
And I add a cube
|
And I add a cube
|
||||||
And the object "Cube" is selected
|
And the object "Cube" is selected
|
||||||
When I press "bim.set_override_colour"
|
When I press "bim.set_override_colour"
|
||||||
Then nothing happens
|
Then nothing happens
|
||||||
|
|
||||||
Scenario: Set viewport shadow from sun
|
Scenario: Set viewport shadow from sun
|
||||||
Given an empty IFC project
|
Given an empty Blender session
|
||||||
And I add a sun
|
And I add a sun
|
||||||
And the object "Sun" is selected
|
And the object "Sun" is selected
|
||||||
When I press "bim.set_viewport_shadow_from_sun"
|
When I press "bim.set_viewport_shadow_from_sun"
|
||||||
Then nothing happens
|
Then nothing happens
|
||||||
|
|
||||||
Scenario: Snap spaces together
|
Scenario: Snap spaces together
|
||||||
Given an empty IFC project
|
Given an empty Blender session
|
||||||
And I add a cube
|
And I add a cube
|
||||||
And I add a cube
|
And I add a cube
|
||||||
And the object "Cube" is selected
|
And the object "Cube" is selected
|
||||||
And additionally the object "Cube.001" is selected
|
And additionally the object "Cube.001" is selected
|
||||||
When I press "bim.snap_spaces_together"
|
When I press "bim.snap_spaces_together"
|
||||||
Then nothing happens
|
Then nothing happens
|
||||||
|
|
||||||
|
Scenario: Resize to storey
|
||||||
|
Given an empty IFC project
|
||||||
|
And I add a cube
|
||||||
|
And the object "Cube" is selected
|
||||||
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||||
|
And I press "bim.assign_class"
|
||||||
|
And the object "IfcWall/Cube" is selected
|
||||||
|
And the variable "storey" is "tool.Ifc.get().by_type('IfcBuildingStorey')[0].id()"
|
||||||
|
And I press "bim.assign_container(structure={storey})"
|
||||||
|
When I press "bim.resize_to_storey"
|
||||||
|
Then nothing happens
|
||||||
|
|||||||
@@ -56,6 +56,13 @@ def material():
|
|||||||
prophet.verify()
|
prophet.verify()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def misc():
|
||||||
|
prophet = Prophecy(blenderbim.core.tool.Misc)
|
||||||
|
yield prophet
|
||||||
|
prophet.verify()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def owner():
|
def owner():
|
||||||
prophet = Prophecy(blenderbim.core.tool.Owner)
|
prophet = Prophecy(blenderbim.core.tool.Owner)
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import blenderbim.core.misc as subject
|
||||||
|
from test.core.bootstrap import misc
|
||||||
|
|
||||||
|
|
||||||
|
class TestResizeToStorey:
|
||||||
|
def test_run(self, misc):
|
||||||
|
misc.get_object_storey("obj").should_be_called().will_return("storey")
|
||||||
|
misc.get_storey_elevation_in_si("storey").should_be_called().will_return("elevation")
|
||||||
|
misc.get_storey_height_in_si("storey").should_be_called().will_return("height")
|
||||||
|
misc.set_object_origin_to_bottom("obj").should_be_called()
|
||||||
|
misc.move_object_to_elevation("obj", "elevation").should_be_called()
|
||||||
|
misc.scale_object_to_height("obj", "height").should_be_called()
|
||||||
|
misc.mark_object_as_edited("obj").should_be_called()
|
||||||
|
subject.resize_to_storey(misc, obj="obj")
|
||||||
|
|
||||||
|
def test_doing_nothing_when_the_object_has_no_storey(self, misc):
|
||||||
|
misc.get_object_storey("obj").should_be_called().will_return(None)
|
||||||
|
subject.resize_to_storey(misc, obj="obj")
|
||||||
|
|
||||||
|
def test_doing_nothing_when_the_storey_has_no_height(self, misc):
|
||||||
|
misc.get_object_storey("obj").should_be_called().will_return("storey")
|
||||||
|
misc.get_storey_height_in_si("storey").should_be_called().will_return(None)
|
||||||
|
subject.resize_to_storey(misc, obj="obj")
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import math
|
||||||
|
import numpy
|
||||||
|
import ifcopenshell
|
||||||
|
import test.bim.bootstrap
|
||||||
|
import blenderbim.core.tool
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
from 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, product=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, product=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, product=storey, relating_object=building)
|
||||||
|
ifcopenshell.api.run("aggregate.assign_object", ifc, product=storey2, relating_object=building)
|
||||||
|
assert subject.get_storey_height_in_si(storey) == 2.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, product=storey, relating_object=building)
|
||||||
|
assert subject.get_storey_height_in_si(storey) 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, product=storey, relating_object=building)
|
||||||
|
assert subject.get_storey_height_in_si(storey) 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 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
|
||||||
Reference in New Issue
Block a user