mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-16 18:44:47 +00:00
You can now add ports to distribution elements in Blender.
This commit is contained in:
@@ -20,6 +20,7 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddPort,
|
||||
operator.AddSystem,
|
||||
operator.AssignSystem,
|
||||
operator.DisableEditingSystem,
|
||||
|
||||
@@ -25,6 +25,7 @@ import blenderbim.tool as tool
|
||||
def refresh():
|
||||
SystemData.is_loaded = False
|
||||
ObjectSystemData.is_loaded = False
|
||||
PortData.is_loaded = False
|
||||
|
||||
|
||||
class SystemData:
|
||||
|
||||
@@ -150,3 +150,12 @@ class HidePorts(bpy.types.Operator, Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
core.hide_ports(tool.System, element=tool.Ifc.get_entity(context.active_object))
|
||||
|
||||
|
||||
class AddPort(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_port"
|
||||
bl_label = "Add Ports"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_port(tool.Ifc, tool.System, element=tool.Ifc.get_entity(context.active_object))
|
||||
|
||||
@@ -153,6 +153,7 @@ class BIM_PT_ports(Panel):
|
||||
row.label(text=f"{PortData.data['total_ports']} Ports Found", icon="PLUGIN")
|
||||
row.operator("bim.show_ports", icon="HIDE_OFF", text="")
|
||||
row.operator("bim.hide_ports", icon="HIDE_ON", text="")
|
||||
row.operator("bim.add_port", icon="ADD", text="")
|
||||
|
||||
|
||||
class BIM_UL_systems(UIList):
|
||||
|
||||
@@ -74,3 +74,10 @@ def show_ports(system, element=None):
|
||||
|
||||
def hide_ports(system, element=None):
|
||||
system.delete_element_objects(system.get_ports(element))
|
||||
|
||||
|
||||
def add_port(ifc, system, element=None):
|
||||
system.load_ports(system.get_ports(element))
|
||||
obj = system.create_empty_at_cursor_with_element_orientation(element)
|
||||
port = system.run_root_assign_class(obj=obj, ifc_class="IfcDistributionPort")
|
||||
ifc.run("system.assign_port", element=element, port=port)
|
||||
|
||||
@@ -380,6 +380,7 @@ class Surveyor:
|
||||
|
||||
@interface
|
||||
class System:
|
||||
def create_empty_at_cursor_with_element_orientation(cls, element): pass
|
||||
def delete_element_objects(cls, elements): pass
|
||||
def disable_editing_system(cls): pass
|
||||
def disable_system_editing_ui(cls): pass
|
||||
@@ -389,6 +390,7 @@ class System:
|
||||
def import_system_attributes(cls, system): pass
|
||||
def import_systems(cls): pass
|
||||
def load_ports(cls, port): pass
|
||||
def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None): pass
|
||||
def select_elements(cls, elements): pass
|
||||
def select_system_products(cls, system): pass
|
||||
def set_active_system(cls, system): pass
|
||||
|
||||
@@ -199,7 +199,15 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None):
|
||||
def run_root_assign_class(
|
||||
cls,
|
||||
obj=None,
|
||||
ifc_class=None,
|
||||
predefined_type=None,
|
||||
should_add_representation=True,
|
||||
context=None,
|
||||
ifc_representation_class=None,
|
||||
):
|
||||
return blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
|
||||
@@ -24,6 +24,15 @@ from blenderbim.bim import import_ifc
|
||||
|
||||
|
||||
class System(blenderbim.core.tool.System):
|
||||
@classmethod
|
||||
def create_empty_at_cursor_with_element_orientation(cls, element):
|
||||
element_obj = tool.Ifc.get_object(element)
|
||||
obj = bpy.data.objects.new("Port", None)
|
||||
obj.matrix_world = element_obj.matrix_world
|
||||
obj.matrix_world.translation = bpy.context.scene.cursor.matrix.translation
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def delete_element_objects(cls, elements):
|
||||
for element in elements:
|
||||
@@ -77,11 +86,34 @@ class System(blenderbim.core.tool.System):
|
||||
ifc_importer.calculate_unit_scale()
|
||||
ports = set(ports)
|
||||
ports -= ifc_importer.create_products(ports)
|
||||
if ports:
|
||||
for port in ports:
|
||||
ifc_importer.create_product(port)
|
||||
for port in ports or []:
|
||||
if tool.Ifc.get_object(port):
|
||||
continue
|
||||
ifc_importer.create_product(port)
|
||||
ifc_importer.place_objects_in_collections()
|
||||
|
||||
@classmethod
|
||||
def run_root_assign_class(
|
||||
cls,
|
||||
obj=None,
|
||||
ifc_class=None,
|
||||
predefined_type=None,
|
||||
should_add_representation=True,
|
||||
context=None,
|
||||
ifc_representation_class=None,
|
||||
):
|
||||
return blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
ifc_class=ifc_class,
|
||||
predefined_type=predefined_type,
|
||||
should_add_representation=should_add_representation,
|
||||
context=context,
|
||||
ifc_representation_class=ifc_representation_class,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def select_elements(cls, elements):
|
||||
for element in elements:
|
||||
|
||||
@@ -103,3 +103,13 @@ class TestHidePorts:
|
||||
system.get_ports("element").should_be_called().will_return(["port"])
|
||||
system.delete_element_objects(["port"]).should_be_called()
|
||||
subject.hide_ports(system, element="element")
|
||||
|
||||
|
||||
class TestAddPort:
|
||||
def test_run(self, ifc, system):
|
||||
system.get_ports("element").should_be_called().will_return(["port"])
|
||||
system.load_ports(["port"]).should_be_called()
|
||||
system.create_empty_at_cursor_with_element_orientation("element").should_be_called().will_return("obj")
|
||||
system.run_root_assign_class(obj="obj", ifc_class="IfcDistributionPort").should_be_called().will_return("port")
|
||||
ifc.run("system.assign_port", element="element", port="port").should_be_called()
|
||||
subject.add_port(ifc, system, element="element")
|
||||
|
||||
@@ -29,6 +29,18 @@ class TestImplementsTool(NewFile):
|
||||
assert isinstance(subject(), blenderbim.core.tool.System)
|
||||
|
||||
|
||||
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)
|
||||
bpy.context.scene.cursor.matrix.translation[0] = 5
|
||||
obj = subject.create_empty_at_cursor_with_element_orientation(element)
|
||||
assert obj.matrix_world.translation[0] == 5
|
||||
|
||||
|
||||
class TestDeleteElementObjects(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
@@ -164,6 +176,11 @@ class TestLoadPorts(NewFile):
|
||||
assert list(obj.location) == [0, 0, 0]
|
||||
|
||||
|
||||
class TestRunAssignClassOperator(NewFile):
|
||||
def test_nothing(self):
|
||||
pass
|
||||
|
||||
|
||||
class TestSelectElements(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.placement
|
||||
|
||||
|
||||
class Usecase:
|
||||
@@ -40,8 +41,14 @@ class Usecase:
|
||||
if self.settings["port"] in rel.RelatedObjects:
|
||||
return
|
||||
|
||||
if not rels:
|
||||
return self.file.create_entity(
|
||||
if rels:
|
||||
rel = rels[0]
|
||||
related_objects = set(rel.RelatedObjects) or set()
|
||||
related_objects.add(self.settings["port"])
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
else:
|
||||
rel = self.file.create_entity(
|
||||
"IfcRelNests",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
@@ -49,21 +56,31 @@ class Usecase:
|
||||
RelatingObject=self.settings["element"],
|
||||
)
|
||||
|
||||
rel = rels[0]
|
||||
related_objects = set(rel.RelatedObjects) or set()
|
||||
related_objects.add(self.settings["port"])
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
self.update_port_placement()
|
||||
|
||||
return rel
|
||||
|
||||
def execute_ifc2x3(self):
|
||||
for rel in self.settings["element"].HasPorts or []:
|
||||
if rel.RelatingPort == self.settings["port"]:
|
||||
return
|
||||
return self.file.create_entity(
|
||||
rel = self.file.create_entity(
|
||||
"IfcRelConnectsPortToElement",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
RelatingPort=self.settings["port"],
|
||||
RelatedElement=self.settings["element"],
|
||||
)
|
||||
self.update_port_placement()
|
||||
return rel
|
||||
|
||||
def update_port_placement(self):
|
||||
placement = getattr(self.settings["port"], "ObjectPlacement", None)
|
||||
if placement and placement.is_a("IfcLocalPlacement"):
|
||||
ifcopenshell.api.run(
|
||||
"geometry.edit_object_placement",
|
||||
self.file,
|
||||
product=self.settings["port"],
|
||||
matrix=ifcopenshell.util.placement.get_local_placement(self.settings["port"].ObjectPlacement),
|
||||
is_si=False,
|
||||
)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import numpy
|
||||
import test.bootstrap
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.system
|
||||
@@ -31,6 +32,38 @@ class TestAssignPort(test.bootstrap.IFC4):
|
||||
ifcopenshell.api.run("system.assign_port", self.file, element=element, port=port)
|
||||
assert ifcopenshell.util.system.get_ports(element) == [port]
|
||||
|
||||
def test_updating_the_placement_to_be_relative_if_it_exists(self):
|
||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
||||
ifcopenshell.api.run("unit.assign_unit", self.file)
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcChiller")
|
||||
subelement = ifcopenshell.api.run("system.add_port", self.file)
|
||||
matrix = numpy.array(
|
||||
(
|
||||
(1.0, 0.0, 0.0, 1.0),
|
||||
(0.0, 1.0, 0.0, 1.0),
|
||||
(0.0, 0.0, 1.0, 1.0),
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
)
|
||||
)
|
||||
submatrix = numpy.array(
|
||||
(
|
||||
(1.0, 0.0, 0.0, 1.0),
|
||||
(0.0, 1.0, 0.0, 2.0),
|
||||
(0.0, 0.0, 1.0, 3.0),
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
)
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.edit_object_placement", self.file, product=element, matrix=matrix.copy(), is_si=False
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.edit_object_placement", self.file, product=subelement, matrix=submatrix.copy(), is_si=False
|
||||
)
|
||||
ifcopenshell.api.run("system.assign_port", self.file, element=element, port=subelement)
|
||||
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement), matrix)
|
||||
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(subelement.ObjectPlacement), submatrix)
|
||||
assert subelement.ObjectPlacement.PlacementRelTo == element.ObjectPlacement
|
||||
|
||||
|
||||
class TestAssignPortIFC2X3(test.bootstrap.IFC2X3):
|
||||
def test_assigning_a_port_once_only(self):
|
||||
@@ -41,3 +74,35 @@ class TestAssignPortIFC2X3(test.bootstrap.IFC2X3):
|
||||
assert ifcopenshell.util.system.get_ports(element) == [port]
|
||||
ifcopenshell.api.run("system.assign_port", self.file, element=element, port=port)
|
||||
assert ifcopenshell.util.system.get_ports(element) == [port]
|
||||
|
||||
def test_updating_the_placement_to_be_relative_if_it_exists(self):
|
||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
||||
ifcopenshell.api.run("unit.assign_unit", self.file)
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcFlowSegment")
|
||||
subelement = ifcopenshell.api.run("system.add_port", self.file)
|
||||
matrix = numpy.array(
|
||||
(
|
||||
(1.0, 0.0, 0.0, 1.0),
|
||||
(0.0, 1.0, 0.0, 1.0),
|
||||
(0.0, 0.0, 1.0, 1.0),
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
)
|
||||
)
|
||||
submatrix = numpy.array(
|
||||
(
|
||||
(1.0, 0.0, 0.0, 1.0),
|
||||
(0.0, 1.0, 0.0, 2.0),
|
||||
(0.0, 0.0, 1.0, 3.0),
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
)
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.edit_object_placement", self.file, product=element, matrix=matrix.copy(), is_si=False
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.edit_object_placement", self.file, product=subelement, matrix=submatrix.copy(), is_si=False
|
||||
)
|
||||
ifcopenshell.api.run("system.assign_port", self.file, element=element, port=subelement)
|
||||
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement), matrix)
|
||||
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(subelement.ObjectPlacement), submatrix)
|
||||
assert subelement.ObjectPlacement.PlacementRelTo == element.ObjectPlacement
|
||||
|
||||
Reference in New Issue
Block a user