mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-18 22:33:33 +00:00
Skeleton of "add fitting" operator which decides appropriate fitting type
This commit is contained in:
@@ -31,6 +31,7 @@ from . import (
|
|||||||
stair,
|
stair,
|
||||||
window,
|
window,
|
||||||
opening,
|
opening,
|
||||||
|
mep,
|
||||||
pie,
|
pie,
|
||||||
workspace,
|
workspace,
|
||||||
profile,
|
profile,
|
||||||
@@ -105,6 +106,7 @@ classes = (
|
|||||||
space.GenerateSpace,
|
space.GenerateSpace,
|
||||||
space.GenerateSpacesFromWalls,
|
space.GenerateSpacesFromWalls,
|
||||||
space.ToggleSpaceVisibility,
|
space.ToggleSpaceVisibility,
|
||||||
|
mep.FitFlowSegments,
|
||||||
prop.BIMModelProperties,
|
prop.BIMModelProperties,
|
||||||
prop.BIMArrayProperties,
|
prop.BIMArrayProperties,
|
||||||
prop.BIMStairProperties,
|
prop.BIMStairProperties,
|
||||||
|
|||||||
@@ -39,7 +39,92 @@ V = lambda *x: Vector([float(i) for i in x])
|
|||||||
float_is_zero = lambda f: 0.0001 >= f >= -0.0001
|
float_is_zero = lambda f: 0.0001 >= f >= -0.0001
|
||||||
|
|
||||||
|
|
||||||
class MEPGenerator:
|
class FitFlowSegments(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.fit_flow_segments"
|
||||||
|
bl_label = "Fit Flow Segments"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
selected_objs = []
|
||||||
|
selected_profiles = []
|
||||||
|
|
||||||
|
selected_class = None
|
||||||
|
for obj in context.selected_objects:
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
if element and element.is_a("IfcFlowSegment"):
|
||||||
|
if selected_class and not element.is_a(selected_class):
|
||||||
|
return # The user is mixing up ducts and pipes.
|
||||||
|
profile = tool.Model.get_flow_segment_profile(element)
|
||||||
|
if profile:
|
||||||
|
selected_profiles.append(profile)
|
||||||
|
selected_objs.append(obj)
|
||||||
|
selected_class = element.is_a()
|
||||||
|
|
||||||
|
total_selected_objs = len(selected_objs)
|
||||||
|
total_profiles = len(set(selected_profiles))
|
||||||
|
fitting_type = None
|
||||||
|
|
||||||
|
if total_selected_objs == 1:
|
||||||
|
fitting_type = "OBSTRUCTION"
|
||||||
|
elif total_selected_objs == 2:
|
||||||
|
# Shorten the axis by the profile size to allow for fuzzy intersections
|
||||||
|
# e.g. if two ducts touch, we want a bend, not a cross.
|
||||||
|
|
||||||
|
axis1 = tool.Model.get_flow_segment_axis(selected_objs[0])
|
||||||
|
profile_size = max(selected_objs[0].dimensions.x, selected_objs[0].dimensions.y)
|
||||||
|
offset = (axis1[1] - axis1[0]).normalized() * profile_size
|
||||||
|
axis1 = (axis1[0] + offset, axis1[1] - offset)
|
||||||
|
|
||||||
|
axis2 = tool.Model.get_flow_segment_axis(selected_objs[1])
|
||||||
|
profile_size = max(selected_objs[1].dimensions.x, selected_objs[1].dimensions.y)
|
||||||
|
offset = (axis2[1] - axis2[0]).normalized() * profile_size
|
||||||
|
axis2 = (axis2[0] + offset, axis2[1] - offset)
|
||||||
|
|
||||||
|
angle = tool.Cad.angle_edges(axis1, axis2, signed=False, degrees=True)
|
||||||
|
is_parallel = tool.Cad.is_x(angle, (0, 180), tolerance=0.001)
|
||||||
|
|
||||||
|
if total_profiles == 1:
|
||||||
|
if is_parallel:
|
||||||
|
return
|
||||||
|
intersect1, intersect2 = tool.Cad.intersect_edges(axis1, axis2)
|
||||||
|
is_on_axis1 = tool.Cad.is_point_on_edge(intersect1, axis1)
|
||||||
|
is_on_axis2 = tool.Cad.is_point_on_edge(intersect2, axis2)
|
||||||
|
if not is_on_axis1 and not is_on_axis2:
|
||||||
|
fitting_type = "BEND"
|
||||||
|
elif is_on_axis1 and is_on_axis2:
|
||||||
|
fitting_type = "CROSS"
|
||||||
|
else:
|
||||||
|
fitting_type = "TEE"
|
||||||
|
elif total_profiles == 2:
|
||||||
|
if is_parallel:
|
||||||
|
fitting_type = "TRANSITION"
|
||||||
|
elif total_selected_objs == 3:
|
||||||
|
if total_profiles > 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
axis1 = tool.Model.get_flow_segment_axis(selected_objs[0])
|
||||||
|
axis2 = tool.Model.get_flow_segment_axis(selected_objs[1])
|
||||||
|
axis3 = tool.Model.get_flow_segment_axis(selected_objs[2])
|
||||||
|
|
||||||
|
angle12 = tool.Cad.angle_edges(axis1, axis2, signed=False, degrees=True)
|
||||||
|
angle13 = tool.Cad.angle_edges(axis1, axis3, signed=False, degrees=True)
|
||||||
|
angle21 = tool.Cad.angle_edges(axis2, axis1, signed=False, degrees=True)
|
||||||
|
angle23 = tool.Cad.angle_edges(axis2, axis3, signed=False, degrees=True)
|
||||||
|
is_parallel12 = tool.Cad.is_x(angle12, (0, 180), tolerance=0.001)
|
||||||
|
is_parallel13 = tool.Cad.is_x(angle13, (0, 180), tolerance=0.001)
|
||||||
|
is_parallel21 = tool.Cad.is_x(angle21, (0, 180), tolerance=0.001)
|
||||||
|
is_parallel23 = tool.Cad.is_x(angle23, (0, 180), tolerance=0.001)
|
||||||
|
|
||||||
|
if not all(is_parallel12, is_parallel13, is_parallel21, is_parallel23):
|
||||||
|
fitting_type = "WYE"
|
||||||
|
|
||||||
|
if not fitting_type:
|
||||||
|
return
|
||||||
|
|
||||||
|
print(fitting_type)
|
||||||
|
|
||||||
|
|
||||||
|
class MepGenerator:
|
||||||
def __init__(self, relating_type=None):
|
def __init__(self, relating_type=None):
|
||||||
self.relating_type = relating_type
|
self.relating_type = relating_type
|
||||||
|
|
||||||
|
|||||||
@@ -318,10 +318,20 @@ class BimToolUI:
|
|||||||
op.depth = cls.props.extrusion_depth
|
op.depth = cls.props.extrusion_depth
|
||||||
|
|
||||||
add_layout_hotkey_operator(cls.layout, "Extend", "S_E", "")
|
add_layout_hotkey_operator(cls.layout, "Extend", "S_E", "")
|
||||||
add_layout_hotkey_operator(cls.layout, "Edit Axis", "A_E", "")
|
|
||||||
add_layout_hotkey_operator(cls.layout, "Butt", "S_T", "")
|
if AuthoringData.data["active_class"] in (
|
||||||
add_layout_hotkey_operator(cls.layout, "Mitre", "S_Y", "")
|
"IfcCableCarrierSegment",
|
||||||
add_layout_hotkey_operator(cls.layout, "Rotate 90", "S_R", bpy.ops.bim.rotate_90.__doc__)
|
"IfcCableSegment",
|
||||||
|
"IfcDuctSegment",
|
||||||
|
"IfcPipeSegment",
|
||||||
|
):
|
||||||
|
add_layout_hotkey_operator(cls.layout, "Extend", "S_E", "")
|
||||||
|
add_layout_hotkey_operator(cls.layout, "Add Fitting", "S_F", "")
|
||||||
|
else:
|
||||||
|
add_layout_hotkey_operator(cls.layout, "Edit Axis", "A_E", "")
|
||||||
|
add_layout_hotkey_operator(cls.layout, "Butt", "S_T", "")
|
||||||
|
add_layout_hotkey_operator(cls.layout, "Mitre", "S_Y", "")
|
||||||
|
add_layout_hotkey_operator(cls.layout, "Rotate 90", "S_R", bpy.ops.bim.rotate_90.__doc__)
|
||||||
add_layout_hotkey_operator(cls.layout, "Regen", "S_G", bpy.ops.bim.recalculate_profile.__doc__)
|
add_layout_hotkey_operator(cls.layout, "Regen", "S_G", bpy.ops.bim.recalculate_profile.__doc__)
|
||||||
row.operator("bim.extend_profile", icon="X", text="").join_type = ""
|
row.operator("bim.extend_profile", icon="X", text="").join_type = ""
|
||||||
|
|
||||||
@@ -358,14 +368,6 @@ class BimToolUI:
|
|||||||
elif AuthoringData.data["active_class"] in ("IfcSpace",):
|
elif AuthoringData.data["active_class"] in ("IfcSpace",):
|
||||||
add_layout_hotkey_operator(cls.layout, "Regen", "S_G", bpy.ops.bim.generate_space.__doc__)
|
add_layout_hotkey_operator(cls.layout, "Regen", "S_G", bpy.ops.bim.generate_space.__doc__)
|
||||||
|
|
||||||
elif AuthoringData.data["active_class"] in (
|
|
||||||
"IfcCableCarrierSegmentType",
|
|
||||||
"IfcCableSegmentType",
|
|
||||||
"IfcDuctSegmentType",
|
|
||||||
"IfcPipeSegmentType",
|
|
||||||
):
|
|
||||||
add_layout_hotkey_operator(cls.layout, "Extend", "S_E", "")
|
|
||||||
|
|
||||||
elif (
|
elif (
|
||||||
(RoofData.is_loaded or not RoofData.load())
|
(RoofData.is_loaded or not RoofData.load())
|
||||||
and RoofData.data["pset_data"]
|
and RoofData.data["pset_data"]
|
||||||
@@ -631,6 +633,8 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bpy.ops.bim.flip_wall()
|
bpy.ops.bim.flip_wall()
|
||||||
elif self.active_class in ("IfcWindow", "IfcWindowStandardCase", "IfcDoor", "IfcDoorStandardCase"):
|
elif self.active_class in ("IfcWindow", "IfcWindowStandardCase", "IfcDoor", "IfcDoorStandardCase"):
|
||||||
bpy.ops.bim.flip_fill()
|
bpy.ops.bim.flip_fill()
|
||||||
|
elif self.active_class in ("IfcDuctSegment", "IfcPipeSegment", "IfcCableCarrierSegment", "IfcCableSegment"):
|
||||||
|
bpy.ops.bim.fit_flow_segments()
|
||||||
|
|
||||||
def hotkey_S_G(self):
|
def hotkey_S_G(self):
|
||||||
if not bpy.context.selected_objects:
|
if not bpy.context.selected_objects:
|
||||||
|
|||||||
@@ -495,6 +495,17 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
items.append(item.FirstOperand)
|
items.append(item.FirstOperand)
|
||||||
return booleans
|
return booleans
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_flow_segment_axis(cls, obj):
|
||||||
|
z_values = [v[2] for v in obj.bound_box]
|
||||||
|
return (obj.matrix_world @ Vector((0, 0, min(z_values))), obj.matrix_world @ Vector((0, 0, max(z_values))))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_flow_segment_profile(cls, element):
|
||||||
|
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
||||||
|
if material and material.is_a("IfcMaterialProfileSet") and len(material.MaterialProfiles) == 1:
|
||||||
|
return material.MaterialProfiles[0].Profile
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_usage_type(cls, element):
|
def get_usage_type(cls, element):
|
||||||
material = ifcopenshell.util.element.get_material(element, should_inherit=False)
|
material = ifcopenshell.util.element.get_material(element, should_inherit=False)
|
||||||
|
|||||||
Reference in New Issue
Block a user