mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-25 22:36:50 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 998f9c6a1e | |||
| 3ff31b577c | |||
| cd013079f4 | |||
| b8a9674483 | |||
| 8beb1bef98 | |||
| 377676f881 | |||
| 4efe284338 | |||
| 34a4e3f37a | |||
| 889c9a9e9f | |||
| 9e79499532 | |||
| 589b98053e | |||
| fc5bb230a9 |
@@ -199,7 +199,7 @@ class MaterialCreator:
|
||||
|
||||
|
||||
class IfcImporter:
|
||||
def __init__(self, ifc_import_settings):
|
||||
def __init__(self, ifc_import_settings: IfcImportSettings):
|
||||
self.ifc_import_settings = ifc_import_settings
|
||||
self.diff = None
|
||||
self.file: ifcopenshell.file = None
|
||||
@@ -725,7 +725,7 @@ class IfcImporter:
|
||||
if len(subelement.Coordinates) == 3 and self.is_point_far_away(subelement, is_meters=False):
|
||||
return True
|
||||
|
||||
def apply_blender_offset_to_matrix_world(self, obj, matrix):
|
||||
def apply_blender_offset_to_matrix_world(self, obj: bpy.types.Object, matrix: np.ndarray) -> mathutils.Matrix:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.has_blender_offset:
|
||||
if obj.data and obj.data.get("has_cartesian_point_offset", None):
|
||||
@@ -953,7 +953,9 @@ class IfcImporter:
|
||||
self.create_product(element, mesh=mesh)
|
||||
|
||||
def create_products(
|
||||
self, products, settings: Optional[ifcopenshell.geom.main.settings] = None
|
||||
self,
|
||||
products: set[ifcopenshell.entity_instance],
|
||||
settings: Optional[ifcopenshell.geom.main.settings] = None,
|
||||
) -> set[ifcopenshell.entity_instance]:
|
||||
results = set()
|
||||
if not products:
|
||||
@@ -1627,27 +1629,27 @@ class IfcImporter:
|
||||
if self.ifc_import_settings.has_filter:
|
||||
rel_aggregates = set()
|
||||
for element in self.elements:
|
||||
if element.IsDecomposedBy:
|
||||
rel_aggregates.add(element.IsDecomposedBy[0])
|
||||
elif element.Decomposes:
|
||||
rel_aggregates.add(element.Decomposes[0])
|
||||
elif getattr(element, "IsNestedBy", []): # IFC2X3 does not have IsNestedBy
|
||||
if [e for e in element.IsNestedBy[0].RelatedObjects if not e.is_a("IfcPort")]:
|
||||
rel_aggregates.add(element.IsNestedBy[0])
|
||||
elif getattr(element, "Nests", []):
|
||||
rel_aggregates.add(element.Nests[0])
|
||||
if decomposed_by := element.IsDecomposedBy:
|
||||
rel_aggregates.add(decomposed_by[0])
|
||||
elif decomposes := element.Decomposes:
|
||||
rel_aggregates.add(decomposes[0])
|
||||
elif nested_by := getattr(element, "IsNestedBy", []): # IFC2X3 does not have IsNestedBy
|
||||
if next((e for e in nested_by[0].RelatedObjects if not e.is_a("IfcPort")), None):
|
||||
rel_aggregates.add(nested_by[0])
|
||||
elif nests := getattr(element, "Nests", []):
|
||||
rel_aggregates.add(nests[0])
|
||||
else:
|
||||
rel_aggregates = [
|
||||
r
|
||||
for r in self.file.by_type("IfcRelAggregates")
|
||||
if r.RelatingObject.is_a("IfcElement") or r.RelatingObject.is_a("IfcElementType")
|
||||
if (relating_obj := r.RelatingObject).is_a("IfcElement") or relating_obj.is_a("IfcElementType")
|
||||
] + [
|
||||
r
|
||||
for r in self.file.by_type("IfcRelNests")
|
||||
if (
|
||||
r.RelatingObject.is_a("IfcElement")
|
||||
or r.RelatingObject.is_a("IfcElementType")
|
||||
or (r.RelatingObject.is_a("IfcPositioningElement") and not r.RelatingObject.is_a("IfcGrid"))
|
||||
(relating_obj := r.RelatingObject).is_a("IfcElement")
|
||||
or relating_obj.is_a("IfcElementType")
|
||||
or (relating_obj.is_a("IfcPositioningElement") and not relating_obj.is_a("IfcGrid"))
|
||||
)
|
||||
and [e for e in r.RelatedObjects if not e.is_a("IfcPort")]
|
||||
]
|
||||
@@ -1810,7 +1812,7 @@ class IfcImporter:
|
||||
if rel.is_a("IfcRelAssignsToGroup") and rel.RelatingGroup.ObjectType == "DRAWING":
|
||||
return rel.RelatingGroup
|
||||
|
||||
def get_element_matrix(self, element: ifcopenshell.entity_instance) -> np.array:
|
||||
def get_element_matrix(self, element: ifcopenshell.entity_instance) -> np.ndarray:
|
||||
if isinstance(element, ifcopenshell.sqlite_entity):
|
||||
result = self.geometry_cache["shapes"][element.id()]["matrix"]
|
||||
else:
|
||||
@@ -1959,14 +1961,14 @@ class IfcImporter:
|
||||
|
||||
print(traceback.format_exc())
|
||||
|
||||
def a2p(self, o, z, x):
|
||||
def a2p(self, o: mathutils.Vector, z: mathutils.Vector, x: mathutils.Vector) -> mathutils.Matrix:
|
||||
y = z.cross(x)
|
||||
r = mathutils.Matrix((x, y, z, o))
|
||||
r.resize_4x4()
|
||||
r.transpose()
|
||||
return r
|
||||
|
||||
def get_axis2placement(self, plc):
|
||||
def get_axis2placement(self, plc: ifcopenshell.entity_instance) -> mathutils.Matrix:
|
||||
if plc.is_a("IfcAxis2Placement3D"):
|
||||
z = mathutils.Vector(plc.Axis.DirectionRatios if plc.Axis else (0, 0, 1))
|
||||
x = mathutils.Vector(plc.RefDirection.DirectionRatios if plc.RefDirection else (1, 0, 0))
|
||||
@@ -1986,7 +1988,7 @@ class IfcImporter:
|
||||
o = plc.LocalOrigin.Coordinates
|
||||
return self.a2p(o, z, x)
|
||||
|
||||
def get_local_placement(self, plc):
|
||||
def get_local_placement(self, plc: Optional[ifcopenshell.entity_instance] = None) -> mathutils.Matrix:
|
||||
if plc is None:
|
||||
return mathutils.Matrix()
|
||||
if plc.PlacementRelTo is None:
|
||||
@@ -2001,11 +2003,11 @@ class IfcImporter:
|
||||
bpy.context.scene.BIMRootProperties.contexts = str(subcontext.id())
|
||||
break
|
||||
|
||||
def link_element(self, element, obj):
|
||||
def link_element(self, element: ifcopenshell.entity_instance, obj: IFC_CONNECTED_TYPE) -> None:
|
||||
self.added_data[element.id()] = obj
|
||||
tool.Ifc.link(element, obj)
|
||||
|
||||
def set_matrix_world(self, obj, matrix_world):
|
||||
def set_matrix_world(self, obj: bpy.types.Object, matrix_world: mathutils.Matrix) -> None:
|
||||
obj.matrix_world = matrix_world
|
||||
tool.Geometry.record_object_position(obj)
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ classes = (
|
||||
operator.AddLayer,
|
||||
operator.AddListItem,
|
||||
operator.AddMaterial,
|
||||
operator.DuplicateMaterial,
|
||||
operator.AddMaterialSet,
|
||||
operator.AddProfile,
|
||||
operator.AssignMaterial,
|
||||
|
||||
@@ -134,6 +134,19 @@ class AddMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
material_prop_purge()
|
||||
|
||||
|
||||
class DuplicateMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.duplicate_material"
|
||||
bl_label = "Diplicate Material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
material: bpy.props.IntProperty(name="Material ID")
|
||||
|
||||
def _execute(self, context):
|
||||
ifc_file = tool.Ifc.get()
|
||||
tool.Material.duplicate_material(ifc_file.by_id(self.material))
|
||||
material_prop_purge()
|
||||
bpy.ops.bim.load_materials()
|
||||
|
||||
|
||||
class AddMaterialSet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_material_set"
|
||||
bl_label = "Add Material Set"
|
||||
|
||||
@@ -61,6 +61,8 @@ class BIM_PT_materials(Panel):
|
||||
if self.props.materials and self.props.active_material_index < len(self.props.materials):
|
||||
material = self.props.materials[self.props.active_material_index]
|
||||
if material.ifc_definition_id:
|
||||
op = row.operator("bim.duplicate_material", text="", icon="DUPLICATE")
|
||||
op.material = material.ifc_definition_id
|
||||
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.material = material.ifc_definition_id
|
||||
op = row.operator("bim.enable_editing_material", text="", icon="GREASEPENCIL")
|
||||
|
||||
@@ -21,6 +21,7 @@ from . import ui, prop, operator, data
|
||||
|
||||
classes = (
|
||||
operator.AddProfileDef,
|
||||
operator.DuplicateProfileDef,
|
||||
operator.DisableEditingArbitraryProfile,
|
||||
operator.DisableEditingProfile,
|
||||
operator.DisableProfileEditingUI,
|
||||
|
||||
@@ -66,9 +66,15 @@ class RemoveProfileDef(bpy.types.Operator, tool.Ifc.Operator):
|
||||
profile: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMProfileProperties
|
||||
current_index = props.active_profile_index
|
||||
ifcopenshell.api.run("profile.remove_profile", tool.Ifc.get(), profile=tool.Ifc.get().by_id(self.profile))
|
||||
bpy.ops.bim.load_profiles()
|
||||
|
||||
# preserve selected index if possible
|
||||
if props.profiles:
|
||||
props.active_profile_index = min(current_index, len(props.profiles) - 1)
|
||||
|
||||
|
||||
class EnableEditingProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_profile"
|
||||
@@ -126,6 +132,27 @@ class AddProfileDef(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bpy.ops.bim.load_profiles()
|
||||
|
||||
|
||||
class DuplicateProfileDef(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.duplicate_profile_def"
|
||||
bl_label = "Duplicate Profile"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BIMProfileProperties
|
||||
if len(props.profiles) > props.active_profile_index:
|
||||
return True
|
||||
cls.poll_message_set("No profile selected to duplicate.")
|
||||
return False
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMProfileProperties
|
||||
ifc_file = tool.Ifc.get()
|
||||
profile = ifc_file.by_id(props.profiles[props.active_profile_index].ifc_definition_id)
|
||||
tool.Profile.duplicate_profile(profile)
|
||||
bpy.ops.bim.load_profiles()
|
||||
|
||||
|
||||
class EnableEditingArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_arbitrary_profile"
|
||||
bl_label = "Enable Editing Arbitrary Profile"
|
||||
|
||||
@@ -70,6 +70,7 @@ class BIM_PT_profiles(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "profile_classes", text="")
|
||||
row.operator("bim.add_profile_def", text="", icon="ADD")
|
||||
row.operator("bim.duplicate_profile_def", icon="DUPLICATE", text="")
|
||||
|
||||
self.layout.template_list(
|
||||
"BIM_UL_profiles",
|
||||
|
||||
@@ -30,6 +30,7 @@ classes = (
|
||||
operator.DisableAddingPresentationStyle,
|
||||
operator.DisableEditingStyle,
|
||||
operator.DisableEditingStyles,
|
||||
operator.DuplicateStyle,
|
||||
operator.EditStyle,
|
||||
operator.EditSurfaceStyle,
|
||||
operator.EnableAddingPresentationStyle,
|
||||
|
||||
@@ -493,6 +493,22 @@ class EnableAddingPresentationStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props.is_adding = True
|
||||
|
||||
|
||||
class DuplicateStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.duplicate_style"
|
||||
bl_label = "Duplicate Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
style: bpy.props.IntProperty(name="Style ID")
|
||||
|
||||
def _execute(self, context):
|
||||
style_type = context.scene.BIMStylesProperties.style_type
|
||||
ifc_file = tool.Ifc.get()
|
||||
style = ifc_file.by_id(self.style)
|
||||
tool.Style.duplicate_style(style)
|
||||
bpy.ops.bim.disable_editing_styles()
|
||||
bpy.ops.bim.load_styles(style_type=style_type)
|
||||
|
||||
|
||||
class DisableAddingPresentationStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_adding_presentation_style"
|
||||
bl_label = "Disable Add Presentation Style"
|
||||
|
||||
@@ -44,19 +44,33 @@ class BIM_PT_styles(Panel):
|
||||
|
||||
self.props = context.scene.BIMStylesProperties
|
||||
|
||||
if self.props.is_editing:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} {}s".format(len(self.props.styles), self.props.style_type), icon="SHADING_RENDERED")
|
||||
if not self.props.is_adding:
|
||||
row.operator("bim.enable_adding_presentation_style", text="", icon="ADD")
|
||||
row.operator("bim.disable_editing_styles", text="", icon="CANCEL")
|
||||
else:
|
||||
if not self.props.is_editing:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Styles".format(StylesData.data["total_styles"]), icon="SHADING_RENDERED")
|
||||
blenderbim.bim.helper.prop_with_search(row, self.props, "style_type", text="")
|
||||
row.operator("bim.load_styles", text="", icon="IMPORT").style_type = self.props.style_type
|
||||
return
|
||||
|
||||
active_style = self.props.styles and self.props.active_style_index < len(self.props.styles)
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} {}s".format(len(self.props.styles), self.props.style_type), icon="SHADING_RENDERED")
|
||||
row.operator("bim.disable_editing_styles", text="", icon="CANCEL")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
if not self.props.is_adding:
|
||||
row.operator("bim.enable_adding_presentation_style", text="", icon="ADD")
|
||||
if active_style:
|
||||
style = self.props.styles[self.props.active_style_index]
|
||||
material_name = StylesData.data["styles_to_blender_material_names"][self.props.active_style_index]
|
||||
material = bpy.data.materials[material_name]
|
||||
|
||||
row.operator("bim.duplicate_style", text="", icon="DUPLICATE").style = style.ifc_definition_id
|
||||
row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF").style = style.ifc_definition_id
|
||||
op = row.operator("bim.enable_editing_style", text="", icon="GREASEPENCIL")
|
||||
op.style = style.ifc_definition_id
|
||||
row.operator("bim.remove_style", text="", icon="X").style = style.ifc_definition_id
|
||||
|
||||
self.layout.template_list("BIM_UL_styles", "", self.props, "styles", self.props, "active_style_index")
|
||||
|
||||
# adding a new IfcSurfaceStyle
|
||||
@@ -77,17 +91,7 @@ class BIM_PT_styles(Panel):
|
||||
row.operator("bim.disable_adding_presentation_style", text="", icon="CANCEL")
|
||||
|
||||
# style ui tools
|
||||
if self.props.styles and self.props.active_style_index < len(self.props.styles):
|
||||
row = self.layout.row(align=True)
|
||||
style = self.props.styles[self.props.active_style_index]
|
||||
material_name = StylesData.data["styles_to_blender_material_names"][self.props.active_style_index]
|
||||
material = bpy.data.materials[material_name]
|
||||
|
||||
op = row.operator("bim.enable_editing_style", text="Edit Style", icon="GREASEPENCIL")
|
||||
op.style = style.ifc_definition_id
|
||||
row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF").style = style.ifc_definition_id
|
||||
row.operator("bim.remove_style", text="", icon="X").style = style.ifc_definition_id
|
||||
|
||||
if active_style:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(material.BIMStyleProperties, "active_style_type", icon="SHADING_RENDERED", text="")
|
||||
op = row.operator("bim.update_current_style", icon="FILE_REFRESH", text="")
|
||||
|
||||
@@ -94,7 +94,12 @@ def add_sheet(ifc, drawing, titleblock=None):
|
||||
def regenerate_sheet(drawing, sheet=None):
|
||||
titleblock_uri = drawing.get_document_uri(sheet, "TITLEBLOCK")
|
||||
drawing.create_svg_sheet(sheet, drawing.sanitise_filename(Path(titleblock_uri).stem))
|
||||
drawing.add_drawings(sheet)
|
||||
try:
|
||||
drawing.add_drawings(sheet)
|
||||
except FileNotFoundError:
|
||||
path_layout = drawing.get_document_uri(sheet, "LAYOUT")
|
||||
if drawing.does_file_exist(path_layout):
|
||||
drawing.delete_file(path_layout)
|
||||
|
||||
|
||||
def open_sheet(drawing, sheet=None):
|
||||
|
||||
@@ -622,7 +622,7 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
obj.data.BIMMeshProperties.material_checksum = str([s.id() for s in cls.get_styles(obj) if s])
|
||||
|
||||
@classmethod
|
||||
def record_object_position(cls, obj):
|
||||
def record_object_position(cls, obj: bpy.types.Object) -> None:
|
||||
# These are recorded separately because they have different numerical tolerances
|
||||
obj.BIMObjectProperties.location_checksum = repr(np.array(obj.matrix_world.translation).tobytes())
|
||||
obj.BIMObjectProperties.rotation_checksum = repr(np.array(obj.matrix_world.to_3x3()).tobytes())
|
||||
|
||||
@@ -38,6 +38,10 @@ class Material(blenderbim.core.tool.Material):
|
||||
def disable_editing_materials(cls):
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def duplicate_material(cls, material: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
return ifcopenshell.util.element.copy_deep(tool.Ifc.get(), material)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_materials(cls):
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = True
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
@@ -75,3 +76,7 @@ class Profile(blenderbim.core.tool.Profile):
|
||||
@classmethod
|
||||
def get_model_profiles(cls):
|
||||
return tool.Ifc.get().by_type("IfcProfileDef")
|
||||
|
||||
@classmethod
|
||||
def duplicate_profile(cls, profile: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
return ifcopenshell.util.element.copy_deep(tool.Ifc.get(), profile)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import numpy as np
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.helper
|
||||
@@ -59,6 +60,10 @@ class Style(blenderbim.core.tool.Style):
|
||||
def disable_editing_styles(cls):
|
||||
bpy.context.scene.BIMStylesProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def duplicate_style(cls, style: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
return ifcopenshell.util.element.copy_deep(tool.Ifc.get(), style)
|
||||
|
||||
@classmethod
|
||||
def enable_editing(cls, obj):
|
||||
obj.BIMStyleProperties.is_editing = True
|
||||
|
||||
@@ -103,6 +103,7 @@ For Linux or Mac:
|
||||
# Remove and link other IfcOpenShell utilities
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifccsv.py
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcdiff.py
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/bsdd.py
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifc4d
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifc5d
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifccityjson
|
||||
@@ -110,9 +111,11 @@ For Linux or Mac:
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcpatch
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifctester
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcfm
|
||||
$ rm -r $BLENDER_ADDON_PATH/libs/Desktop
|
||||
|
||||
$ ln -s $PWD/src/ifccsv/ifccsv.py $BLENDER_ADDON_PATH/libs/site/packages/ifccsv.py
|
||||
$ ln -s $PWD/src/ifcdiff/ifcdiff.py $BLENDER_ADDON_PATH/libs/site/packages/ifcdiff.py
|
||||
$ ln -s $PWD/src/bsdd/bsdd.py $BLENDER_ADDON_PATH/libs/site/packages/bsdd.py
|
||||
$ ln -s $PWD/src/ifc4d/ifc4d $BLENDER_ADDON_PATH/libs/site/packages/ifc4d
|
||||
$ ln -s $PWD/src/ifc5d/ifc5d $BLENDER_ADDON_PATH/libs/site/packages/ifc5d
|
||||
$ ln -s $PWD/src/ifccityjson/ifccityjson $BLENDER_ADDON_PATH/libs/site/packages/ifccityjson
|
||||
@@ -120,6 +123,7 @@ For Linux or Mac:
|
||||
$ ln -s $PWD/src/ifcpatch/ifcpatch $BLENDER_ADDON_PATH/libs/site/packages/ifcpatch
|
||||
$ ln -s $PWD/src/ifctester/ifctester $BLENDER_ADDON_PATH/libs/site/packages/ifctester
|
||||
$ ln -s $PWD/src/ifcfm/ifcfm $BLENDER_ADDON_PATH/libs/site/packages/ifcfm
|
||||
$ ln -s $PWD/src/blenderbim/blenderbim/libs/desktop $BLENDER_ADDON_PATH/libs/Desktop
|
||||
|
||||
# Manually download some third party dependencies
|
||||
$ cd $BLENDER_ADDON_PATH/bim/data/gantt
|
||||
@@ -168,6 +172,7 @@ Before running it follow the instructions descibed after `rem` tags.
|
||||
echo Remove and link other IfcOpenShell utilities...
|
||||
del "%blenderbim%\libs\site\packages\ifccsv.py"
|
||||
del "%blenderbim%\libs\site\packages\ifcdiff.py"
|
||||
del "%blenderbim%\libs\site\packages\bsdd.py"
|
||||
rd /S /Q "%blenderbim%\libs\site\packages\ifc4d"
|
||||
rd /S /Q "%blenderbim%\libs\site\packages\ifc5d"
|
||||
rd /S /Q "%blenderbim%\libs\site\packages\ifccityjson"
|
||||
@@ -175,9 +180,11 @@ Before running it follow the instructions descibed after `rem` tags.
|
||||
rd /S /Q "%blenderbim%\libs\site\packages\ifcpatch"
|
||||
rd /S /Q "%blenderbim%\libs\site\packages\ifctester"
|
||||
rd /S /Q "%blenderbim%\libs\site\packages\ifcfm"
|
||||
rd /S /Q "%blenderbim%\libs\desktop"
|
||||
|
||||
mklink "%blenderbim%\libs\site\packages\ifccsv.py" "%cd%\src\ifccsv\ifccsv.py"
|
||||
mklink "%blenderbim%\libs\site\packages\ifcdiff.py" "%cd%\src\ifcdiff\ifcdiff.py"
|
||||
mklink "%blenderbim%\libs\site\packages\bsdd.py" "%cd%\src\bsdd\bsdd.py"
|
||||
mklink /D "%blenderbim%\libs\site\packages\ifc4d" "%cd%\src\ifc4d\ifc4d"
|
||||
mklink /D "%blenderbim%\libs\site\packages\ifc5d" "%cd%\src\ifc5d\ifc5d"
|
||||
mklink /D "%blenderbim%\libs\site\packages\ifccityjson" "%cd%\src\ifccityjson\ifccityjson"
|
||||
@@ -185,6 +192,7 @@ Before running it follow the instructions descibed after `rem` tags.
|
||||
mklink /D "%blenderbim%\libs\site\packages\ifcpatch" "%cd%\src\ifcpatch\ifcpatch"
|
||||
mklink /D "%blenderbim%\libs\site\packages\ifctester" "%cd%\src\ifctester\ifctester"
|
||||
mklink /D "%blenderbim%\libs\site\packages\ifcfm" "%cd%\src\ifcfm\ifcfm"
|
||||
mklink /D "%blenderbim%\libs\desktop" "%cd%\src\blenderbim\blenderbim\libs\desktop"
|
||||
|
||||
echo Manually downloading some third party dependencies...
|
||||
curl https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js -o "%blenderbim%\bim\data\gantt\jsgantt.js"
|
||||
|
||||
@@ -39,6 +39,7 @@ import sys
|
||||
import tempfile
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import ifcopenshell.util.file
|
||||
|
||||
@@ -197,12 +198,14 @@ def register_schema(schema):
|
||||
register_schema_attributes(schema.schema)
|
||||
|
||||
|
||||
def schema_by_name(schema=None, schema_version=None):
|
||||
def schema_by_name(
|
||||
schema: Optional[str] = None, schema_version: Optional[tuple[int, ...]] = None
|
||||
) -> ifcopenshell_wrapper.schema_definition:
|
||||
"""Returns an object allowing you to query the IFC schema itself
|
||||
|
||||
:param schema: Which IFC schema to use, chosen from "IFC2X3", "IFC4",
|
||||
or "IFC4X3". These refer to the ISO approved versions of IFC.
|
||||
:type schema: string
|
||||
:type schema: string, optional
|
||||
:param schema_version: If you want to specify an exact version of IFC
|
||||
that may not be an ISO approved version, use this argument instead
|
||||
of ``schema``. IFC versions on technical.buildingsmart.org are
|
||||
@@ -211,13 +214,15 @@ def schema_by_name(schema=None, schema_version=None):
|
||||
ADD2 TC1, which is the official version approved by ISO when people
|
||||
refer to "IFC4". Generally you should not use this argument unless
|
||||
you are testing non-ISO IFC releases.
|
||||
:type schema_version: tuple[int]
|
||||
:type schema_version: tuple[int, ...], optional
|
||||
:return: Schema definition object.
|
||||
:rtype: ifocpenshell_wrapper.schema_definition
|
||||
"""
|
||||
if schema_version:
|
||||
prefixes = ("IFC", "X", "_ADD", "_TC")
|
||||
schema = "".join("".join(map(str, t)) if t[1] else "" for t in zip(prefixes, schema_version))
|
||||
else:
|
||||
schema = {"IFC4X3": "IFC4X3_ADD1"}.get(schema, schema)
|
||||
schema = {"IFC4X3": "IFC4X3_ADD2"}.get(schema, schema)
|
||||
return ifcopenshell_wrapper.schema_by_name(schema)
|
||||
|
||||
|
||||
|
||||
@@ -17,12 +17,19 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime
|
||||
import ifcopenshell.util.constraint
|
||||
import ifcopenshell.util.date
|
||||
import ifcopenshell.util.sequence
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, task_time=None, attributes=None):
|
||||
def __init__(
|
||||
self,
|
||||
file: ifcopenshell.file,
|
||||
task_time: ifcopenshell.entity_instance,
|
||||
attributes: Optional[dict[str, Any]] = None,
|
||||
):
|
||||
"""Edits the attributes of an IfcTaskTime
|
||||
|
||||
For more information about the attributes and data types of an
|
||||
@@ -55,7 +62,7 @@ class Usecase:
|
||||
self.file = file
|
||||
self.settings = {"task_time": task_time, "attributes": attributes or {}}
|
||||
|
||||
def execute(self):
|
||||
def execute(self) -> None:
|
||||
self.task = self.get_task()
|
||||
self.calendar = ifcopenshell.util.sequence.derive_calendar(self.task)
|
||||
|
||||
@@ -169,12 +176,12 @@ class Usecase:
|
||||
duration, "IfcDuration"
|
||||
)
|
||||
|
||||
def get_task(self):
|
||||
return [
|
||||
def get_task(self) -> ifcopenshell.entity_instance:
|
||||
return next(
|
||||
e
|
||||
for e in self.file.get_inverse(self.settings["task_time"])
|
||||
if e.is_a("IfcTask")
|
||||
][0]
|
||||
)
|
||||
|
||||
def handle_resource_calculation(self):
|
||||
resources = ifcopenshell.util.sequence.get_task_resources(self.task, is_deep=False)
|
||||
|
||||
@@ -17,10 +17,16 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell.util.date
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, work_time=None, attributes=None):
|
||||
def __init__(
|
||||
self,
|
||||
file: ifcopenshell.file,
|
||||
work_time: ifcopenshell.entity_instance,
|
||||
attributes: Optional[dict[str, Any]] = None,
|
||||
):
|
||||
"""Edits the attributes of an IfcWorkTime
|
||||
|
||||
For more information about the attributes and data types of an
|
||||
@@ -53,13 +59,15 @@ class Usecase:
|
||||
self.file = file
|
||||
self.settings = {"work_time": work_time, "attributes": attributes or {}}
|
||||
|
||||
def execute(self):
|
||||
def execute(self) -> None:
|
||||
for name, value in self.settings["attributes"].items():
|
||||
if name in ("Start", "StartDate"):
|
||||
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
|
||||
# 4 IfcWorktime Start
|
||||
self.settings["work_time"][4] = value
|
||||
elif name in ("Finish", "FinishDate"):
|
||||
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
|
||||
# 5 IfcWorktime Finish
|
||||
self.settings["work_time"][5] = value
|
||||
else:
|
||||
setattr(self.settings["work_time"], name, value)
|
||||
|
||||
@@ -28,7 +28,7 @@ import numbers
|
||||
import zipfile
|
||||
import functools
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
from typing import Optional, Any
|
||||
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.file
|
||||
@@ -52,7 +52,7 @@ class Transaction:
|
||||
self.batch_delete_ids = set()
|
||||
self.batch_inverses = []
|
||||
|
||||
def serialise_entity_instance(self, element):
|
||||
def serialise_entity_instance(self, element: ifcopenshell.entity_instance) -> dict[str, Any]:
|
||||
info = element.get_info()
|
||||
for key, value in info.items():
|
||||
info[key] = self.serialise_value(element, value)
|
||||
@@ -103,7 +103,7 @@ class Transaction:
|
||||
}
|
||||
)
|
||||
|
||||
def store_delete(self, element):
|
||||
def store_delete(self, element: ifcopenshell.entity_instance) -> None:
|
||||
inverses = {}
|
||||
if self.is_batched:
|
||||
if element.id() not in self.batch_delete_ids:
|
||||
|
||||
@@ -20,8 +20,10 @@ import json
|
||||
from pathlib import Path
|
||||
import copy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.ifcopenshell_wrapper as ifcopenshell_wrapper
|
||||
import ifcopenshell.util.attribute
|
||||
import ifcopenshell.util.schema
|
||||
from typing import Optional, Literal
|
||||
|
||||
try:
|
||||
import glob
|
||||
@@ -58,8 +60,8 @@ IFC4x3_SPEC_URL_TEMPLATE = "https://ifc43-docs.standards.buildingsmart.org/IFC/R
|
||||
# child -> description
|
||||
# note: in IFC4x3 there is no children[] for properties
|
||||
|
||||
|
||||
SCHEMA_FILES = {
|
||||
SUPPORTED_SCHEMA = Literal["IFC2X3", "IFC4", "IFC4X3"]
|
||||
SCHEMA_FILES: dict[SUPPORTED_SCHEMA, dict] = {
|
||||
"IFC2X3": {
|
||||
"entities": BASE_MODULE_PATH / "schema/ifc2x3_entities.json",
|
||||
"properties": BASE_MODULE_PATH / "schema/ifc2x3_properties.json",
|
||||
@@ -81,7 +83,11 @@ SCHEMA_FILES = {
|
||||
}
|
||||
|
||||
db = None
|
||||
schema_by_name = {"IFC2X3": None, "IFC4": None, "IFC4X3": None}
|
||||
schema_by_name: dict[SUPPORTED_SCHEMA, Optional[ifcopenshell_wrapper.schema_definition]] = {
|
||||
"IFC2X3": None,
|
||||
"IFC4": None,
|
||||
"IFC4X3": None,
|
||||
}
|
||||
|
||||
|
||||
def get_db(version):
|
||||
@@ -103,11 +109,12 @@ def get_db(version):
|
||||
return db.get(version)
|
||||
|
||||
|
||||
def get_schema_by_name(version: str):
|
||||
def get_schema_by_name(version: str) -> ifcopenshell_wrapper.schema_definition:
|
||||
global schema_by_name
|
||||
version = ifcopenshell.util.schema.get_fallback_schema(version)
|
||||
if not schema_by_name[version]:
|
||||
schema_by_name[version] = ifcopenshell.ifcopenshell_wrapper.schema_by_name(version)
|
||||
schema_name = "IFC4X3_ADD2" if version == "IFC4X3" else version
|
||||
schema_by_name[version] = ifcopenshell_wrapper.schema_by_name(schema_name)
|
||||
return schema_by_name[version]
|
||||
|
||||
|
||||
|
||||
@@ -121,10 +121,10 @@ def get_local_placement(placement: ifcopenshell.entity_instance) -> MatrixType:
|
||||
"""
|
||||
if placement is None:
|
||||
return np.eye(4)
|
||||
if placement.PlacementRelTo is None:
|
||||
if (rel_to := placement.PlacementRelTo) is None:
|
||||
parent = np.eye(4)
|
||||
else:
|
||||
parent = get_local_placement(placement.PlacementRelTo)
|
||||
parent = get_local_placement(rel_to)
|
||||
return np.dot(parent, get_axis2placement(placement.RelativePlacement))
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,20 @@ import datetime
|
||||
import ifcopenshell.util.date
|
||||
from math import floor
|
||||
from functools import lru_cache
|
||||
from collections import namedtuple
|
||||
from typing import Union, Literal, Optional, Iterator
|
||||
|
||||
|
||||
DURATION_TYPE = Literal["ELAPSEDTIME", "WORKTIME", "NOTDEFINED"]
|
||||
RECURRENCE_TYPE = Literal[
|
||||
"BY_DAY_COUNT",
|
||||
"BY_WEEKDAY_COUNT",
|
||||
"DAILY",
|
||||
"MONTHLY_BY_DAY_OF_MONTH",
|
||||
"MONTHLY_BY_POSITION",
|
||||
"WEEKLY",
|
||||
"YEARLY_BY_DAY_OF_MONTH",
|
||||
"YEARLY_BY_POSITION",
|
||||
]
|
||||
|
||||
|
||||
def derive_date(task, attribute_name, date=None, is_earliest=False, is_latest=False):
|
||||
@@ -49,7 +62,7 @@ def derive_date(task, attribute_name, date=None, is_earliest=False, is_latest=Fa
|
||||
return date
|
||||
|
||||
|
||||
def derive_calendar(task):
|
||||
def derive_calendar(task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
calendar = get_calendar(task)
|
||||
if calendar:
|
||||
return calendar
|
||||
@@ -57,7 +70,7 @@ def derive_calendar(task):
|
||||
return derive_calendar(rel.RelatingObject)
|
||||
|
||||
|
||||
def get_calendar(task):
|
||||
def get_calendar(task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
calendar = [
|
||||
rel.RelatingControl
|
||||
for rel in task.HasAssignments or []
|
||||
@@ -68,7 +81,7 @@ def get_calendar(task):
|
||||
return calendar[0]
|
||||
|
||||
|
||||
def count_working_days(start, finish, calendar):
|
||||
def count_working_days(start, finish, calendar: ifcopenshell.entity_instance) -> int:
|
||||
result = 0
|
||||
if start == finish:
|
||||
return 0
|
||||
@@ -88,7 +101,11 @@ def count_working_days(start, finish, calendar):
|
||||
|
||||
|
||||
def get_start_or_finish_date(
|
||||
start, duration, duration_type, calendar, date_type="FINISH"
|
||||
start,
|
||||
duration,
|
||||
duration_type: DURATION_TYPE,
|
||||
calendar: ifcopenshell.entity_instance,
|
||||
date_type: Literal["START", "FINISH"] = "FINISH",
|
||||
):
|
||||
if not duration.days:
|
||||
# Typically a milestone will have zero duration, so the start == finish
|
||||
@@ -107,7 +124,7 @@ def get_start_or_finish_date(
|
||||
return datetime.datetime.combine(result, datetime.time(17))
|
||||
|
||||
|
||||
def offset_date(start, duration, duration_type, calendar):
|
||||
def offset_date(start, duration, duration_type: DURATION_TYPE, calendar: ifcopenshell.entity_instance):
|
||||
current_date = start
|
||||
months = getattr(duration, "months", 0)
|
||||
years = getattr(duration, "years", 0)
|
||||
@@ -129,7 +146,7 @@ def offset_date(start, duration, duration_type, calendar):
|
||||
return current_date
|
||||
|
||||
|
||||
def get_soonest_working_day(start, duration_type, calendar):
|
||||
def get_soonest_working_day(start, duration_type: DURATION_TYPE, calendar: ifcopenshell.entity_instance):
|
||||
if duration_type == "ELAPSEDTIME" or not is_calendar_applicable(start, calendar):
|
||||
return start
|
||||
while not is_working_day(start, calendar):
|
||||
@@ -139,7 +156,7 @@ def get_soonest_working_day(start, duration_type, calendar):
|
||||
return start
|
||||
|
||||
|
||||
def get_recent_working_day(start, duration_type, calendar):
|
||||
def get_recent_working_day(start, duration_type: DURATION_TYPE, calendar: ifcopenshell.entity_instance):
|
||||
if duration_type == "ELAPSEDTIME" or not is_calendar_applicable(start, calendar):
|
||||
return start
|
||||
while not is_working_day(start, calendar):
|
||||
@@ -150,7 +167,7 @@ def get_recent_working_day(start, duration_type, calendar):
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_working_day(day, calendar):
|
||||
def is_working_day(day, calendar: ifcopenshell.entity_instance) -> bool:
|
||||
is_working_day = False
|
||||
for work_time in calendar.WorkingTimes or []:
|
||||
if is_work_time_applicable_to_day(work_time, day):
|
||||
@@ -166,7 +183,7 @@ def is_working_day(day, calendar):
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_calendar_applicable(day, calendar):
|
||||
def is_calendar_applicable(day, calendar: ifcopenshell.entity_instance) -> bool:
|
||||
if not calendar or not calendar.WorkingTimes:
|
||||
return False
|
||||
is_applicable = False
|
||||
@@ -177,18 +194,20 @@ def is_calendar_applicable(day, calendar):
|
||||
return is_applicable
|
||||
|
||||
|
||||
def is_day_in_work_time(day, work_time):
|
||||
def is_day_in_work_time(day, work_time: ifcopenshell.entity_instance) -> bool:
|
||||
is_day_in_work_time = True
|
||||
if isinstance(day, datetime.datetime):
|
||||
day = datetime.date(day.year, day.month, day.day)
|
||||
if work_time[4]:
|
||||
start = ifcopenshell.util.date.ifc2datetime(work_time[4])
|
||||
# 4 IfcWorktime Start
|
||||
if start := work_time[4]:
|
||||
start = ifcopenshell.util.date.ifc2datetime(start)
|
||||
if day > start:
|
||||
is_day_in_work_time = True
|
||||
else:
|
||||
is_day_in_work_time = False
|
||||
if work_time[5]:
|
||||
finish = ifcopenshell.util.date.ifc2datetime(work_time[5])
|
||||
# 5 IfcWorktime Finish
|
||||
if finish := work_time[5]:
|
||||
finish = ifcopenshell.util.date.ifc2datetime(finish)
|
||||
if day < finish:
|
||||
is_day_in_work_time = True
|
||||
else:
|
||||
@@ -196,7 +215,7 @@ def is_day_in_work_time(day, work_time):
|
||||
return is_day_in_work_time
|
||||
|
||||
|
||||
def is_work_time_applicable_to_day(work_time, day):
|
||||
def is_work_time_applicable_to_day(work_time: ifcopenshell.entity_instance, day) -> bool:
|
||||
if not is_day_in_work_time(day, work_time):
|
||||
return False
|
||||
if not work_time.RecurrencePattern:
|
||||
@@ -205,36 +224,39 @@ def is_work_time_applicable_to_day(work_time, day):
|
||||
if isinstance(day, datetime.datetime):
|
||||
day = datetime.date(day.year, day.month, day.day)
|
||||
recurrence = work_time.RecurrencePattern
|
||||
if recurrence.RecurrenceType == "DAILY":
|
||||
recurrence_type: RECURRENCE_TYPE = recurrence.RecurrenceType
|
||||
if recurrence_type == "DAILY":
|
||||
if not recurrence.Interval and not recurrence.Occurrences:
|
||||
return True
|
||||
# 4 IfcWorktime Start
|
||||
if not work_time[4]:
|
||||
return False
|
||||
return False # TODO
|
||||
elif recurrence.RecurrenceType == "WEEKLY":
|
||||
elif recurrence_type == "WEEKLY":
|
||||
if not recurrence.Interval and not recurrence.Occurrences:
|
||||
return (day.weekday() + 1) in recurrence.WeekdayComponent
|
||||
# 4 IfcWorktime Start
|
||||
if not work_time[4]:
|
||||
return False
|
||||
return False # TODO
|
||||
elif recurrence.RecurrenceType == "MONTHLY_BY_DAY_OF_MONTH":
|
||||
elif recurrence_type == "MONTHLY_BY_DAY_OF_MONTH":
|
||||
if not recurrence.Interval and not recurrence.Occurrences:
|
||||
return day.day in recurrence.DayComponent
|
||||
return False # TODO
|
||||
elif recurrence.RecurrenceType == "MONTHLY_BY_POSITION":
|
||||
elif recurrence_type == "MONTHLY_BY_POSITION":
|
||||
if not recurrence.Interval and not recurrence.Occurrences:
|
||||
return (day.weekday() + 1) in recurrence.WeekdayComponent and floor(
|
||||
day.day / 7
|
||||
) + 1 == recurrence["Position"]
|
||||
return False # TODO
|
||||
elif recurrence.RecurrenceType == "YEARLY_BY_DAY_OF_MONTH":
|
||||
elif recurrence_type == "YEARLY_BY_DAY_OF_MONTH":
|
||||
if not recurrence.Interval and not recurrence.Occurrences:
|
||||
return (
|
||||
day.month in recurrence.MonthComponent
|
||||
and day.day in recurrence.DayComponent
|
||||
)
|
||||
return False # TODO
|
||||
elif recurrence.RecurrenceType == "YEARLY_BY_POSITION":
|
||||
elif recurrence_type == "YEARLY_BY_POSITION":
|
||||
if not recurrence.Interval and not recurrence.Occurrences:
|
||||
return (
|
||||
day.month in recurrence.MonthComponent
|
||||
@@ -244,7 +266,7 @@ def is_work_time_applicable_to_day(work_time, day):
|
||||
return False # TODO
|
||||
|
||||
|
||||
def get_task_work_schedule(task):
|
||||
def get_task_work_schedule(task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
parent_task = get_parent_task(task)
|
||||
if parent_task:
|
||||
return get_task_work_schedule(parent_task) or get_task_work_schedule(task)
|
||||
@@ -257,25 +279,23 @@ def get_task_work_schedule(task):
|
||||
return None
|
||||
|
||||
|
||||
def get_nested_tasks(task):
|
||||
def get_nested_tasks(task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return [object for rel in task.IsNestedBy or [] for object in rel.RelatedObjects]
|
||||
|
||||
|
||||
def get_parent_task(task):
|
||||
return (
|
||||
task.Nests[0].RelatingObject
|
||||
if task.Nests and task.Nests[0].RelatingObject.is_a("IfcTask")
|
||||
else None
|
||||
)
|
||||
def get_parent_task(task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
nests = task.Nests
|
||||
if nests and (obj := nests[0].RelatingObject).is_a("IfcTask"):
|
||||
return obj
|
||||
|
||||
|
||||
def get_all_nested_tasks(task):
|
||||
def get_all_nested_tasks(task: ifcopenshell.entity_instance) -> Iterator[ifcopenshell.entity_instance]:
|
||||
for nested_task in get_nested_tasks(task):
|
||||
yield nested_task
|
||||
yield from get_all_nested_tasks(nested_task)
|
||||
|
||||
|
||||
def get_work_schedule_tasks(work_schedule):
|
||||
def get_work_schedule_tasks(work_schedule: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
tasks = []
|
||||
for root_task in get_root_tasks(work_schedule):
|
||||
nested_tasks = get_all_nested_tasks(root_task)
|
||||
@@ -283,7 +303,7 @@ def get_work_schedule_tasks(work_schedule):
|
||||
return tasks
|
||||
|
||||
|
||||
def get_root_tasks(work_schedule):
|
||||
def get_root_tasks(work_schedule: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return [
|
||||
obj
|
||||
for rel in work_schedule.Controls
|
||||
@@ -292,7 +312,7 @@ def get_root_tasks(work_schedule):
|
||||
]
|
||||
|
||||
|
||||
def get_root_tasks_ids(work_schedule):
|
||||
def get_root_tasks_ids(work_schedule: ifcopenshell.entity_instance) -> list[int]:
|
||||
return [
|
||||
obj.id()
|
||||
for rel in work_schedule.Controls
|
||||
@@ -301,7 +321,7 @@ def get_root_tasks_ids(work_schedule):
|
||||
]
|
||||
|
||||
|
||||
def guess_date_range(work_schedule):
|
||||
def guess_date_range(work_schedule: ifcopenshell.entity_instance):
|
||||
earliest = None
|
||||
latest = None
|
||||
root_tasks = get_root_tasks(work_schedule)
|
||||
@@ -323,7 +343,7 @@ def guess_date_range(work_schedule):
|
||||
return earliest, latest
|
||||
|
||||
|
||||
def get_direct_task_outputs(task):
|
||||
def get_direct_task_outputs(task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return [
|
||||
rel.RelatingProduct
|
||||
for rel in task.HasAssignments
|
||||
@@ -331,7 +351,7 @@ def get_direct_task_outputs(task):
|
||||
]
|
||||
|
||||
|
||||
def get_task_outputs(task, is_deep=False):
|
||||
def get_task_outputs(task: ifcopenshell.entity_instance, is_deep=False):
|
||||
if not is_deep:
|
||||
return get_direct_task_outputs(task)
|
||||
else:
|
||||
@@ -342,7 +362,7 @@ def get_task_outputs(task, is_deep=False):
|
||||
]
|
||||
|
||||
|
||||
def get_task_inputs(task, is_deep=False):
|
||||
def get_task_inputs(task: ifcopenshell.entity_instance, is_deep=False):
|
||||
if not is_deep:
|
||||
return [
|
||||
object
|
||||
@@ -365,7 +385,7 @@ def get_task_inputs(task, is_deep=False):
|
||||
]
|
||||
|
||||
|
||||
def get_task_resources(task, is_deep=False):
|
||||
def get_task_resources(task: ifcopenshell.entity_instance, is_deep=False):
|
||||
if not is_deep:
|
||||
return [
|
||||
object
|
||||
@@ -388,15 +408,17 @@ def get_task_resources(task, is_deep=False):
|
||||
]
|
||||
|
||||
|
||||
def has_task_outputs(task):
|
||||
def has_task_outputs(task: ifcopenshell.entity_instance) -> bool:
|
||||
return len(get_task_outputs(task)) > 0
|
||||
|
||||
|
||||
def has_task_inputs(task):
|
||||
def has_task_inputs(task: ifcopenshell.entity_instance) -> bool:
|
||||
return len(get_task_inputs(task)) > 0
|
||||
|
||||
|
||||
def get_tasks_for_product(product, schedule=None):
|
||||
def get_tasks_for_product(
|
||||
product: ifcopenshell.entity_instance, schedule: Optional[ifcopenshell.entity_instance] = None
|
||||
) -> tuple[list[ifcopenshell.entity_instance], list[ifcopenshell.entity_instance]]:
|
||||
"""
|
||||
Get all tasks assigned to or referenced by the given product.
|
||||
|
||||
@@ -438,7 +460,7 @@ def get_tasks_for_product(product, schedule=None):
|
||||
return inputs, outputs
|
||||
|
||||
|
||||
def get_sequence_assignment(task, sequence="successor"):
|
||||
def get_sequence_assignment(task: ifcopenshell.entity_instance, sequence="successor"):
|
||||
if sequence == "successor":
|
||||
relationship_attr = "IsPredecessorTo"
|
||||
elif sequence == "predecessor":
|
||||
|
||||
@@ -72,9 +72,16 @@
|
||||
|
||||
template <>
|
||||
std::string cast_pyobject(PyObject* element) {
|
||||
#if SWIG_VERSION >= 0x040200
|
||||
PyObject *pbytes = NULL;
|
||||
const char* str_data = SWIG_PyUnicode_AsUTF8AndSize(element, NULL, &pbytes);
|
||||
std::string str = str_data;
|
||||
Py_XDECREF(pbytes);
|
||||
#else
|
||||
char* str_data = SWIG_Python_str_AsChar(element);
|
||||
std::string str = str_data;
|
||||
SWIG_Python_str_DelForPy3(str_data);
|
||||
#endif
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user