mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-23 21:38:35 +00:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 840039ef1e | |||
| 84a749605e | |||
| c1283d2ab0 | |||
| 07b5c4918b | |||
| 703f0a6b70 | |||
| 5e98dee46f | |||
| b2eddf55d2 | |||
| db7be640d4 | |||
| 04fa597844 | |||
| 694dbeb96c | |||
| 7afd358c3c | |||
| b0a5a570a9 | |||
| c45b84c850 | |||
| d575a43aee | |||
| 0b151ea559 | |||
| 926970b256 | |||
| d58af0bfa5 | |||
| 429da7c6f3 | |||
| 60a7433573 | |||
| 08a5d444d4 | |||
| 1982069b69 | |||
| 7c06c65df3 | |||
| 6263766202 | |||
| 66fbbeff54 | |||
| 3c5ad1328f | |||
| 3345d7e088 | |||
| 2bb25cb874 | |||
| 444a728e7a | |||
| 70bea0c865 | |||
| 6eea1e9035 | |||
| 4deb1836ad | |||
| 4c8007dcfe | |||
| 000116bb3d | |||
| a2b6f21c7a | |||
| a374ee39d3 | |||
| 0f5c82dffd | |||
| 268fed761e | |||
| fe01d252db | |||
| dc36d74337 | |||
| 7a0da7bd8e | |||
| 530d5a4e63 | |||
| 2d847d1704 | |||
| 631d8fcfb1 | |||
| a8888efb88 | |||
| a51ecea9b1 | |||
| 4d49517111 | |||
| d5d1b3567b | |||
| 7827ce3448 | |||
| a05059db01 | |||
| b78490ca56 |
@@ -4,6 +4,7 @@ set MY_PY_VER=%PY_VER:.=%
|
||||
set LIBXML2="%LIBRARY_PREFIX%/lib/libxml2.lib"
|
||||
|
||||
cmake -G "Ninja" ^
|
||||
-D SCHEMA_VERSIONS="2x3;4;4x1;4x3;4x3_add1" ^
|
||||
-D CMAKE_BUILD_TYPE:STRING=Release ^
|
||||
-D CMAKE_INSTALL_PREFIX:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
-D CMAKE_PREFIX_PATH:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
|
||||
@@ -13,6 +13,7 @@ if [ `uname` == Darwin ]; then
|
||||
fi
|
||||
|
||||
cmake -G Ninja \
|
||||
-DSCHEMA_VERSIONS="2x3;4;4x1;4x3;4x3_add1" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=$PREFIX \
|
||||
${CMAKE_PLATFORM_FLAGS[@]} \
|
||||
|
||||
+3
-3
@@ -36,10 +36,10 @@ requirements:
|
||||
|
||||
run:
|
||||
- python
|
||||
- boost-cpp
|
||||
- occt ==7.7.0
|
||||
- {{ pin_compatible('occt', max_pin='x.x.x') }}
|
||||
- {{ pin_compatible('cgal-cpp', max_pin='x.x.x') }}
|
||||
- {{ pin_compatible('boost-cpp', max_pin='x.x.x') }}
|
||||
- libxml2
|
||||
- cgal-cpp
|
||||
- hdf5
|
||||
- mpfr
|
||||
- gmp # [unix]
|
||||
|
||||
@@ -145,6 +145,8 @@ classes = [
|
||||
ui.BIM_PT_tab_services_object,
|
||||
# Structural analysis
|
||||
ui.BIM_PT_tab_structural,
|
||||
# Construction scheduling
|
||||
ui.BIM_PT_tab_4D5D,
|
||||
# Facility management
|
||||
ui.BIM_PT_tab_handover,
|
||||
ui.BIM_PT_tab_operations,
|
||||
|
||||
@@ -35,7 +35,6 @@ classes = (
|
||||
operator.ViewBrickItem,
|
||||
operator.SerializeBrick,
|
||||
operator.AddBrickNamespace,
|
||||
operator.SetBrickListRoot,
|
||||
operator.RemoveBrickRelation,
|
||||
prop.Brick,
|
||||
prop.BIMBrickProperties,
|
||||
|
||||
@@ -37,6 +37,7 @@ class LoadBrickProject(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.load_brick_project"
|
||||
bl_label = "Load Brickschema Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Load in a Brick project from a file"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
filter_glob: bpy.props.StringProperty(default="*.ttl", options={"HIDDEN"})
|
||||
|
||||
@@ -56,6 +57,7 @@ class ViewBrickClass(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.view_brick_class"
|
||||
bl_label = "View Brick Class"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Inspect the subclasses of this class"
|
||||
brick_class: bpy.props.StringProperty(name="Brick Class")
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
|
||||
@@ -67,6 +69,7 @@ class ViewBrickItem(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.view_brick_item"
|
||||
bl_label = "View Brick Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Inspect this entity in the viewer"
|
||||
item: bpy.props.StringProperty(name="Brick Item")
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
|
||||
@@ -78,6 +81,7 @@ class RewindBrickClass(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.rewind_brick_class"
|
||||
bl_label = "Rewind Brick Class"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Go back to the previous list view"
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
|
||||
def _execute(self, context):
|
||||
@@ -88,6 +92,7 @@ class CloseBrickProject(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.close_brick_project"
|
||||
bl_label = "Close Brick Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Close the Brick project"
|
||||
|
||||
def _execute(self, context):
|
||||
core.close_brick_project(tool.Brick)
|
||||
@@ -122,6 +127,7 @@ class AddBrick(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_brick"
|
||||
bl_label = "Add Brick"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create the Brick entity"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
@@ -143,6 +149,7 @@ class AddBrickRelation(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_brick_relation"
|
||||
bl_label = "Add Brick Relation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create the Brick relationship"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
@@ -178,6 +185,7 @@ class NewBrickFile(bpy.types.Operator):
|
||||
bl_idname = "bim.new_brick_file"
|
||||
bl_label = "New Brick File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create a Brick project from scratch"
|
||||
|
||||
def execute(self, context):
|
||||
IfcStore.begin_transaction(self)
|
||||
@@ -210,16 +218,18 @@ class RefreshBrickViewer(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.refresh_brick_viewer"
|
||||
bl_label = "Refresh Brick Viewer"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
bl_description = "Refresh the list view"
|
||||
|
||||
def _execute(self, context):
|
||||
core.refresh_brick_viewer(tool.Brick, split_screen=self.split_screen)
|
||||
core.refresh_brick_viewer(tool.Brick)
|
||||
core.refresh_brick_viewer(tool.Brick, split_screen=True)
|
||||
|
||||
|
||||
class RemoveBrick(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.remove_brick"
|
||||
bl_label = "Remove Brick"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Delete this entity"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
@@ -262,6 +272,7 @@ class SerializeBrick(bpy.types.Operator):
|
||||
class AddBrickNamespace(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_brick_namespace"
|
||||
bl_label = "Add Brick Namespace"
|
||||
bl_description = "Bind a new namespace to the Brick project"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
@@ -270,24 +281,11 @@ class AddBrickNamespace(bpy.types.Operator, Operator):
|
||||
core.add_namespace(tool.Brick, alias=alias, uri=uri)
|
||||
|
||||
|
||||
class SetBrickListRoot(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.set_brick_list_root"
|
||||
bl_label = "Set Brick View Type"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
|
||||
def _execute(self, context):
|
||||
if self.split_screen:
|
||||
root = context.scene.BIMBrickProperties.split_screen_brick_list_root
|
||||
else:
|
||||
root = context.scene.BIMBrickProperties.brick_list_root
|
||||
core.set_brick_list_root(tool.Brick, brick_root=root, split_screen=self.split_screen)
|
||||
|
||||
|
||||
class RemoveBrickRelation(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.remove_brick_relation"
|
||||
bl_label = "Remove Relation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Delete this relationship"
|
||||
predicate: bpy.props.StringProperty(name="Relation")
|
||||
object: bpy.props.StringProperty(name="Object")
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
import blenderbim.core.brick as core
|
||||
import blenderbim.tool.brick as tool
|
||||
from blenderbim.tool.brick import BrickStore
|
||||
|
||||
def update_active_brick_index(self, context):
|
||||
@@ -65,6 +67,15 @@ def get_brick_relations(self, context):
|
||||
return BrickStore.relationships
|
||||
|
||||
|
||||
def update_view(self, context):
|
||||
root = context.scene.BIMBrickProperties.brick_list_root
|
||||
core.set_brick_list_root(tool.Brick, brick_root=root, split_screen=False)
|
||||
|
||||
def split_screen_update_view(self, context):
|
||||
root = context.scene.BIMBrickProperties.split_screen_brick_list_root
|
||||
core.set_brick_list_root(tool.Brick, brick_root=root, split_screen=True)
|
||||
|
||||
|
||||
class Brick(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
label: StringProperty(name="Label")
|
||||
@@ -79,7 +90,7 @@ class BIMBrickProperties(PropertyGroup):
|
||||
active_brick_index: IntProperty(name="Active Brick Index", update=update_active_brick_index)
|
||||
libraries: EnumProperty(name="Libraries", items=get_libraries)
|
||||
set_list_root_toggled: BoolProperty(name="Set List Root Toggled", default=False)
|
||||
brick_list_root: EnumProperty(name="Brick List Root", items=get_brick_roots)
|
||||
brick_list_root: EnumProperty(name="Brick List Root", items=get_brick_roots, update=update_view)
|
||||
# namespace manager
|
||||
namespace: EnumProperty(name="Namespace", items=get_namespaces)
|
||||
brick_settings_toggled: BoolProperty(name="Brick Settings Toggled", default=False)
|
||||
@@ -102,4 +113,4 @@ class BIMBrickProperties(PropertyGroup):
|
||||
split_screen_active_brick_index: IntProperty(name="Split Screen Active Brick Index", update=update_active_brick_index)
|
||||
split_screen_active_brick_class: StringProperty(name="Split Screen Active Brick Class")
|
||||
split_screen_brick_breadcrumbs: CollectionProperty(name="Split Screen Brick Breadcrumbs", type=StrProperty)
|
||||
split_screen_brick_list_root: EnumProperty(name="Split Screen Brick List Root", items=get_brick_roots)
|
||||
split_screen_brick_list_root: EnumProperty(name="Split Screen Brick List Root", items=get_brick_roots, update=split_screen_update_view)
|
||||
@@ -42,9 +42,17 @@ class BIM_PT_brickschema(Panel):
|
||||
row.operator("bim.load_brick_project", text="Load Project")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
if BrickStore.path:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=BrickStore.path, icon="FILEBROWSER")
|
||||
else:
|
||||
row.label(text="No file", icon="FILEBROWSER")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
if BrickStore.last_saved:
|
||||
row.label(text=BrickStore.last_saved, icon="TIME")
|
||||
else:
|
||||
row.label(text="Not saved", icon="TIME")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.serialize_brick", icon="EXPORT", text="Save")
|
||||
@@ -55,7 +63,7 @@ class BIM_PT_brickschema(Panel):
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(data=self.props, property="brick_settings_toggled", text="", icon="PREFERENCES")
|
||||
|
||||
|
||||
if self.props.brick_settings_toggled:
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
@@ -86,47 +94,43 @@ class BIM_PT_brickschema(Panel):
|
||||
row.prop(data=self.props, property="new_brick_label", text="")
|
||||
prop_with_search(row, self.props, "brick_entity_class", text="")
|
||||
row.operator("bim.add_brick", text="", icon="ADD")
|
||||
# row.operator("bim.refresh_brick_viewer", text="", icon="FILE_REFRESH")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
row.prop(data=self.props, property="set_list_root_toggled", text="", icon="OUTLINER")
|
||||
row.prop(data=self.props, property="split_screen_toggled", text="", icon="WINDOW")
|
||||
row.operator("bim.refresh_brick_viewer", text="", icon="FILE_REFRESH")
|
||||
|
||||
grid = self.layout.grid_flow(even_columns=True)
|
||||
grid1 = grid.column(align=True)
|
||||
row = grid1.row(align=True)
|
||||
grid_left = grid.column(align=True)
|
||||
row = grid_left.row(align=True)
|
||||
if len(self.props.brick_breadcrumbs):
|
||||
op = row.operator("bim.rewind_brick_class", text="", icon="FRAME_PREV")
|
||||
op.split_screen = False
|
||||
row.prop(data=self.props, property="set_list_root_toggled", text="", icon="OUTLINER")
|
||||
row.label(text=self.props.active_brick_class)
|
||||
|
||||
if self.props.set_list_root_toggled:
|
||||
row = grid1.row(align=True)
|
||||
op = row.operator("bim.set_brick_list_root", text="Set View")
|
||||
op.split_screen = False
|
||||
row = grid_left.row(align=True)
|
||||
row.prop(data=self.props, property="brick_list_root", text="")
|
||||
|
||||
row = grid1.row()
|
||||
row = grid_left.row()
|
||||
BIM_UL_bricks.split_screen = False
|
||||
row.template_list("BIM_UL_bricks", "", self.props, "bricks", self.props, "active_brick_index")
|
||||
|
||||
if self.props.split_screen_toggled:
|
||||
grid2 = grid.column(align=True)
|
||||
row = grid2.row(align=True)
|
||||
grid_right = grid.column(align=True)
|
||||
row = grid_right.row(align=True)
|
||||
if len(self.props.split_screen_brick_breadcrumbs):
|
||||
op = row.operator("bim.rewind_brick_class", text="", icon="FRAME_PREV")
|
||||
op.split_screen = True
|
||||
row.label(text=self.props.split_screen_active_brick_class)
|
||||
|
||||
if self.props.set_list_root_toggled:
|
||||
row = grid2.row(align=True)
|
||||
op = row.operator("bim.set_brick_list_root", text="Set View")
|
||||
op.split_screen = True
|
||||
row = grid_right.row(align=True)
|
||||
row.prop(data=self.props, property="split_screen_brick_list_root", text="")
|
||||
|
||||
row = grid2.row()
|
||||
row = grid_right.row()
|
||||
BIM_UL_bricks.split_screen = True
|
||||
row.template_list("BIM_UL_bricks", "", self.props, "split_screen_bricks", self.props, "split_screen_active_brick_index")
|
||||
|
||||
@@ -165,12 +169,10 @@ class BIM_PT_brickschema(Panel):
|
||||
prop_with_search(row, self.props, "new_brick_relation_type", text="")
|
||||
row.prop(data=self.props, property="new_brick_relation_object", text="")
|
||||
row.operator("bim.add_brick_relation", text="", icon="ADD")
|
||||
|
||||
|
||||
if self.props.brick_create_relations_toggled and self.props.add_relation_failed:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Failed to find this entity!", icon="ERROR")
|
||||
|
||||
|
||||
for relation in BrickschemaData.data["active_relations"]:
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
@@ -20,64 +20,65 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddCostColumn,
|
||||
operator.AddCostItem,
|
||||
operator.AddCostItemQuantity,
|
||||
operator.AddCostSchedule,
|
||||
operator.RemoveCostSchedule,
|
||||
operator.EditCostSchedule,
|
||||
operator.AddCostValue,
|
||||
operator.AddCurrency,
|
||||
operator.AddSummaryCostItem,
|
||||
operator.AssignCostItemQuantity,
|
||||
operator.AssignCostItemType,
|
||||
operator.AssignCostValue,
|
||||
operator.CalculateCostItemResourceValue,
|
||||
operator.ChangeParentCostItem,
|
||||
operator.ClearCostItemAssignments,
|
||||
operator.ContractCostItem,
|
||||
operator.ContractCostItemRate,
|
||||
operator.ContractCostItems,
|
||||
operator.CopyCostItem,
|
||||
operator.CopyCostItemValues,
|
||||
operator.DisableEditingCostItem,
|
||||
operator.DisableEditingCostItemQuantity,
|
||||
operator.DisableEditingCostItemValue,
|
||||
operator.DisableEditingCostSchedule,
|
||||
operator.EditCostItem,
|
||||
operator.EditCostItemQuantity,
|
||||
operator.EditCostItemValue,
|
||||
operator.EditCostItemValueFormula,
|
||||
operator.EnableEditingCostSchedule,
|
||||
operator.EnableEditingCostItems,
|
||||
operator.EditCostSchedule,
|
||||
operator.EnableEditingCostItem,
|
||||
operator.ExportCostSchedules,
|
||||
operator.ExpandCostItems,
|
||||
operator.EnableEditingCostItemQuantities,
|
||||
operator.EnableEditingCostItemQuantity,
|
||||
operator.EnableEditingCostItemValues,
|
||||
operator.EnableEditingCostItems,
|
||||
operator.EnableEditingCostItemValue,
|
||||
operator.EnableEditingCostItemValueFormula,
|
||||
operator.DisableEditingCostItem,
|
||||
operator.DisableEditingCostSchedule,
|
||||
operator.DisableEditingCostItemQuantity,
|
||||
operator.DisableEditingCostItemValue,
|
||||
operator.AddCostColumn,
|
||||
operator.RemoveCostColumn,
|
||||
operator.AddCostItem,
|
||||
operator.AddSummaryCostItem,
|
||||
operator.EnableEditingCostItemValues,
|
||||
operator.EnableEditingCostSchedule,
|
||||
operator.ExpandCostItem,
|
||||
operator.ContractCostItem,
|
||||
operator.ExpandCostItemRate,
|
||||
operator.ExpandCostItems,
|
||||
operator.ExportCostSchedules,
|
||||
operator.HighlightProductCostItem,
|
||||
operator.ImportCostScheduleCsv,
|
||||
operator.LoadCostItemElementQuantities,
|
||||
operator.LoadCostItemQuantities,
|
||||
operator.LoadCostItemResourceQuantities,
|
||||
operator.LoadCostItemTaskQuantities,
|
||||
operator.LoadCostItemTypes,
|
||||
operator.LoadProductCostItems,
|
||||
operator.LoadScheduleOfRates,
|
||||
operator.RemoveCostColumn,
|
||||
operator.RemoveCostItem,
|
||||
operator.AssignCostItemType,
|
||||
operator.UnassignCostItemType,
|
||||
operator.AssignCostItemQuantity,
|
||||
operator.UnassignCostItemQuantity,
|
||||
operator.AddCostItemQuantity,
|
||||
operator.RemoveCostItemQuantity,
|
||||
operator.AddCostValue,
|
||||
operator.RemoveCostItemValue,
|
||||
operator.CopyCostItemValues,
|
||||
operator.RemoveCostSchedule,
|
||||
operator.ReorderCostItem,
|
||||
operator.SelectCostItemProducts,
|
||||
operator.SelectCostScheduleProducts,
|
||||
operator.ImportCostScheduleCsv,
|
||||
operator.LoadCostItemQuantities,
|
||||
operator.LoadCostItemTypes,
|
||||
operator.AssignCostValue,
|
||||
operator.LoadScheduleOfRates,
|
||||
operator.ExpandCostItemRate,
|
||||
operator.ContractCostItemRate,
|
||||
operator.CalculateCostItemResourceValue,
|
||||
operator.ClearCostItemAssignments,
|
||||
operator.HighlightProductCostItem,
|
||||
operator.LoadProductCostItems,
|
||||
operator.ReorderCostItem,
|
||||
operator.SelectUnassignedProducts,
|
||||
operator.LoadCostItemElementQuantities,
|
||||
operator.LoadCostItemTaskQuantities,
|
||||
operator.LoadCostItemResourceQuantities,
|
||||
operator.ChangeParentCostItem,
|
||||
operator.CopyCostItem,
|
||||
operator.AddCurrency,
|
||||
operator.UnassignCostItemQuantity,
|
||||
operator.UnassignCostItemType,
|
||||
prop.CostItem,
|
||||
prop.CostItemQuantity,
|
||||
prop.CostItemType,
|
||||
|
||||
@@ -156,6 +156,17 @@ class ContractCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.contract_cost_item(tool.Cost, cost_item=tool.Ifc.get().by_id(self.cost_item))
|
||||
|
||||
|
||||
class ContractCostItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.contract_cost_items"
|
||||
bl_label = "Contract Cost Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Collapse cost item tree"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.contract_cost_items(tool.Cost)
|
||||
|
||||
|
||||
class RemoveCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_cost_item"
|
||||
bl_label = "Remove Cost Item"
|
||||
|
||||
@@ -21,7 +21,6 @@ import blenderbim.bim.module.cost.prop as CostProp
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.cost.data import CostSchedulesData
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
class BIM_PT_cost_schedules(Panel):
|
||||
@@ -30,10 +29,12 @@ class BIM_PT_cost_schedules(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_4D5D"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get() and tool.Ifc.get().schema != "IFC2X3"
|
||||
file = IfcStore.get_file()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
if not CostSchedulesData.is_loaded:
|
||||
@@ -46,8 +47,8 @@ class BIM_PT_cost_schedules(Panel):
|
||||
row.label(text=f"{CostSchedulesData.data['total_cost_schedules']} Cost Schedules Found", icon="TEXT")
|
||||
row.operator("bim.export_cost_schedules", text="Export as spreadsheet", icon="EXPORT")
|
||||
else:
|
||||
row.label(text="No Cost Schedules found.", icon="COMMUNITY")
|
||||
row = self.layout.row()
|
||||
row.label(text="No Cost Schedules found.", icon="TEXT")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(self.props, "cost_schedule_predefined_types")
|
||||
row.operator("bim.add_cost_schedule", icon="ADD", text="Add")
|
||||
@@ -138,8 +139,8 @@ class BIM_PT_cost_schedules(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.add_summary_cost_item", text="Add Summary Cost", icon="ADD")
|
||||
row.operator("bim.expand_all_tasks", text="Expand All")
|
||||
row.operator("bim.contract_all_tasks", text="Contract All")
|
||||
row.operator("bim.expand_cost_items", text="Expand All")
|
||||
row.operator("bim.contract_cost_items", text="Contract All")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
if self.props.cost_items and self.props.active_cost_item_index < len(self.props.cost_items):
|
||||
|
||||
@@ -129,9 +129,8 @@ class ExportCsvAttributes(bpy.types.Operator):
|
||||
class ExportIfcCsv(bpy.types.Operator):
|
||||
bl_idname = "bim.export_ifccsv"
|
||||
bl_label = "Export IFC"
|
||||
#filename_ext = ".csv"
|
||||
filename_ext = ".csv"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
|
||||
def invoke(self, context, event):
|
||||
props = context.scene.CsvProperties
|
||||
@@ -149,8 +148,7 @@ class ExportIfcCsv(bpy.types.Operator):
|
||||
ifc_file = IfcStore.get_file()
|
||||
else:
|
||||
ifc_file = ifcopenshell.open(props.csv_ifc_file)
|
||||
selector = ifcopenshell.util.selector.Selector()
|
||||
results = selector.parse(ifc_file, props.ifc_selector)
|
||||
results = ifcopenshell.util.selector.filter_elements(ifc_file, props.ifc_selector)
|
||||
ifc_csv = ifccsv.IfcCsv()
|
||||
attributes = [a.name for a in props.csv_attributes]
|
||||
sep = props.csv_custom_delimiter if props.csv_delimiter == "CUSTOM" else props.csv_delimiter
|
||||
@@ -196,9 +194,12 @@ class EyedropIfcCsv(bpy.types.Operator):
|
||||
global_ids = []
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in context.selected_objects:
|
||||
if hasattr(obj, "BIMObjectProperties") and obj.BIMObjectProperties.ifc_definition_id:
|
||||
global_ids.append("#" + self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId)
|
||||
context.scene.CsvProperties.ifc_selector = "|".join(global_ids)
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
global_id = getattr(element, "GlobalId", None)
|
||||
if global_id:
|
||||
global_ids.append(global_id)
|
||||
context.scene.CsvProperties.ifc_selector = ",".join(global_ids)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -166,6 +166,7 @@ class Annotator:
|
||||
|
||||
center = camera.matrix_world.inverted() @ bpy.context.scene.cursor.location
|
||||
center.z = 0
|
||||
center = camera.matrix_world @ center
|
||||
|
||||
return (
|
||||
center + z_offset,
|
||||
|
||||
@@ -520,7 +520,11 @@ class BaseDecorator:
|
||||
return matrix.inverted()[i].to_3d().normalized()
|
||||
|
||||
text_dir_world_x_axis = get_basis_vector(obj.matrix_world)
|
||||
text_dir = (camera.matrix_world.inverted().to_quaternion() @ text_dir_world_x_axis).to_2d().normalized()
|
||||
camera_matrix = camera.matrix_world.copy()
|
||||
camera_matrix[0][0] = 1
|
||||
camera_matrix[1][1] = 1
|
||||
camera_matrix[2][2] = 1
|
||||
text_dir = (camera_matrix.inverted().to_quaternion() @ text_dir_world_x_axis).to_2d().normalized()
|
||||
|
||||
pos = location_3d_to_region_2d(region, region3d, text_world_position)
|
||||
props = obj.BIMTextProperties
|
||||
|
||||
@@ -485,7 +485,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element)
|
||||
|
||||
self.setup_serialiser(ifc)
|
||||
self.setup_serialiser(ifc, target_view)
|
||||
cache = IfcStore.get_cache()
|
||||
[cache.remove(guid) for guid in invalidated_guids]
|
||||
tree = ifcopenshell.geom.tree()
|
||||
@@ -829,7 +829,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
return svg_path
|
||||
|
||||
def setup_serialiser(self, ifc):
|
||||
def setup_serialiser(self, ifc, target_view):
|
||||
self.svg_settings = ifcopenshell.geom.settings(
|
||||
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
||||
)
|
||||
@@ -853,6 +853,8 @@ class CreateDrawing(bpy.types.Operator):
|
||||
self.serialiser.setScale(self.scale)
|
||||
self.serialiser.setSubtractionSettings(ifcopenshell.ifcopenshell_wrapper.ALWAYS)
|
||||
self.serialiser.setUsePrefiltering(True) # See #3359
|
||||
if target_view == "REFLECTED_PLAN_VIEW":
|
||||
self.serialiser.setMirrorY(True)
|
||||
# tree = ifcopenshell.geom.tree()
|
||||
# This instructs the tree to explode BReps into faces and return
|
||||
# the style of the face when running tree.select_ray()
|
||||
@@ -1512,6 +1514,9 @@ class RemoveDrawing(bpy.types.Operator, Operator):
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id) for d in context.scene.DocProperties.drawings if d.is_selected
|
||||
]
|
||||
else:
|
||||
if not self.drawing:
|
||||
self.report({"ERROR"}, "No drawing selected")
|
||||
return {"CANCELLED"}
|
||||
drawings = [tool.Ifc.get().by_id(self.drawing)]
|
||||
|
||||
removed_drawings = [drawing.id() for drawing in drawings]
|
||||
@@ -1862,7 +1867,7 @@ class RemoveSchedule(bpy.types.Operator, Operator):
|
||||
schedule: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.remove_document(tool.Ifc, tool.Drawing, "SCHEDULE", schedule=tool.Ifc.get().by_id(self.schedule))
|
||||
core.remove_document(tool.Ifc, tool.Drawing, "SCHEDULE", document=tool.Ifc.get().by_id(self.schedule))
|
||||
|
||||
|
||||
class OpenSchedule(bpy.types.Operator, Operator):
|
||||
|
||||
@@ -351,7 +351,7 @@ class Scheduler:
|
||||
wrap_text: if True, text will be wrapped to fit in cell
|
||||
cell_width: width of cell, used for wrapping text
|
||||
"""
|
||||
text_lines = [str(p).upper() for p in p_tags]
|
||||
text_lines = [str(p) for p in p_tags]
|
||||
box_alignment_params = SvgWriter.get_box_alignment_parameters(box_alignment)
|
||||
text_params = {
|
||||
"font-size": font_size,
|
||||
|
||||
@@ -791,7 +791,14 @@ class SvgWriter:
|
||||
return matrix.inverted()[i].to_3d().normalized()
|
||||
|
||||
text_dir_world_x_axis = get_basis_vector(text_obj.matrix_world)
|
||||
text_dir = (self.camera.matrix_world.inverted().to_quaternion() @ text_dir_world_x_axis).to_2d().normalized()
|
||||
|
||||
# RCP cameras may be scaled, so reset scales.
|
||||
camera_matrix = self.camera.matrix_world.copy()
|
||||
camera_matrix[0][0] = 1
|
||||
camera_matrix[1][1] = 1
|
||||
camera_matrix[2][2] = 1
|
||||
|
||||
text_dir = (camera_matrix.inverted().to_quaternion() @ text_dir_world_x_axis).to_2d().normalized()
|
||||
angle = math.degrees(-text_dir.angle_signed(Vector((1, 0))))
|
||||
|
||||
classes = self.get_attribute_classes(text_obj)
|
||||
|
||||
@@ -314,7 +314,7 @@ class BIM_PT_references(Panel):
|
||||
|
||||
if not self.props.is_editing_references:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{DocumentsData.data['total_references']} References Found", icon="LONGDISPLAY")
|
||||
row.label(text=f"{DocumentsData.data['total_references']} References Found", icon="OBJECT_HIDDEN")
|
||||
row.operator("bim.load_references", text="", icon="IMPORT")
|
||||
return
|
||||
|
||||
|
||||
@@ -73,29 +73,68 @@ class RegenerateDistributionElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
[e for e in ifcopenshell.util.system.get_connected_from(element) if e not in processed_elements]
|
||||
)
|
||||
|
||||
if len(connected) == 1:
|
||||
extend_branch(list(connected)[0], branch, element)
|
||||
else:
|
||||
for connected_element in connected:
|
||||
branch_element["children"].append(extend_branch(connected_element, [], element))
|
||||
for connected_element in connected:
|
||||
branch_element["children"].append(extend_branch(connected_element, [], element))
|
||||
|
||||
return branch
|
||||
|
||||
queue = extend_branch(current_element, [])[0]["children"]
|
||||
extended_branch = extend_branch(current_element, [])
|
||||
queue = extended_branch[0]["children"]
|
||||
|
||||
# import pprint
|
||||
# pprint.pprint(queue)
|
||||
|
||||
def get_connected_ports_between(element1, element2):
|
||||
ports1 = tool.System.get_ports(element1)
|
||||
ports2 = tool.System.get_ports(element2)
|
||||
|
||||
for p in ports1:
|
||||
connected_port = tool.System.get_connected_port(p)
|
||||
# in IFC2X3 there is no PredefinedType
|
||||
if getattr(p, "PredefinedType", None) == "WIRELESS":
|
||||
continue
|
||||
if connected_port in ports2:
|
||||
return p, connected_port
|
||||
|
||||
return None, None
|
||||
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
def process_branch(branch):
|
||||
for branch_element in branch:
|
||||
element = branch_element["element"]
|
||||
print("processing", element)
|
||||
predecessor = branch_element["predecessor"]
|
||||
if False: # If the element does not need to be transformed, return early.
|
||||
return
|
||||
|
||||
# Perform the extend, translate, rotate, etc the element as necessary based on the predecessor.
|
||||
# For segments, prioritise extensions instead of translations.
|
||||
# For everything else, only translate. No rotation.
|
||||
# For everything besides segments, only translate. No rotation.
|
||||
|
||||
obj = tool.Ifc.get_object(element)
|
||||
obj_pred = tool.Ifc.get_object(predecessor)
|
||||
if tool.Ifc.is_moved(obj):
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
if tool.Ifc.is_moved(obj_pred):
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj_pred)
|
||||
|
||||
port, port_pred = get_connected_ports_between(element, predecessor)
|
||||
port_matrix_pred = tool.Model.get_element_matrix(port_pred)
|
||||
|
||||
# Only segments can be extended
|
||||
# extension for them takes priority over translation
|
||||
if element.is_a("IfcFlowSegment"):
|
||||
DumbProfileJoiner().join_E(obj, port_matrix_pred.translation * si_conversion)
|
||||
context.view_layer.update() # update since extrusion might involve changing object's location
|
||||
|
||||
port_martix = tool.Model.get_element_matrix(port)
|
||||
port_location = port_martix.translation
|
||||
port_location_pred = port_matrix_pred.translation
|
||||
if not tool.Cad.are_vectors_equal(port_location, port_location_pred):
|
||||
obj.location += (port_location_pred - port_location) * si_conversion
|
||||
context.view_layer.update() # otherwise tool.Ifc.is_moved won't get triggered
|
||||
else:
|
||||
# If the element does not need to be transformed, return early.
|
||||
return
|
||||
|
||||
for child_branch in branch_element["children"]:
|
||||
process_branch(child_branch)
|
||||
|
||||
@@ -168,6 +207,7 @@ class FitFlowSegments(bpy.types.Operator, tool.Ifc.Operator):
|
||||
elif total_profiles == 2:
|
||||
if is_parallel:
|
||||
fitting_type = "TRANSITION"
|
||||
bpy.ops.bim.mep_add_transition()
|
||||
|
||||
elif total_selected_objs == 3:
|
||||
if total_profiles > 1:
|
||||
@@ -229,6 +269,10 @@ class MEPGenerator:
|
||||
if port_position == "end_port":
|
||||
tool.Model.edit_element_placement(port, end_port_matrix)
|
||||
|
||||
continue
|
||||
|
||||
# NOTE: currently this functionality is moved to bim.regenerate_distribution_element
|
||||
|
||||
connected_port = tool.System.get_connected_port(port)
|
||||
if not connected_port:
|
||||
continue
|
||||
|
||||
@@ -40,10 +40,6 @@ from pprint import pprint
|
||||
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRoofType.htm
|
||||
|
||||
|
||||
def float_is_zero(f):
|
||||
return 0.0001 >= f >= -0.0001
|
||||
|
||||
|
||||
def bm_mesh_clean_up(bm):
|
||||
# remove internal edges and faces
|
||||
# adding missing faces so we could rely on `e.is_boundary` later
|
||||
@@ -99,7 +95,7 @@ def is_valid_roof_footprint(bm):
|
||||
# should be bmesh to support edit mode
|
||||
bm.verts.ensure_lookup_table()
|
||||
base_z = bm.verts[0].co.z
|
||||
all_verts_same_level = all([float_is_zero(v.co.z - base_z) for v in bm.verts[1:]])
|
||||
all_verts_same_level = all([tool.Cad.is_x(v.co.z - base_z, 0) for v in bm.verts[1:]])
|
||||
if not all_verts_same_level:
|
||||
return (
|
||||
{"ERROR"},
|
||||
@@ -202,7 +198,7 @@ def generate_hiped_roof_bmesh(
|
||||
|
||||
def find_identical_new_vert(co):
|
||||
for v in bm.verts:
|
||||
if float_is_zero((co - v.co).length):
|
||||
if tool.Cad.is_x((co - v.co).length, 0):
|
||||
return v
|
||||
|
||||
def find_other_polygon_verts(edge):
|
||||
@@ -234,7 +230,7 @@ def generate_hiped_roof_bmesh(
|
||||
bottom_chords_to_remove = []
|
||||
|
||||
def is_footprint_vert(v):
|
||||
return float_is_zero(v.co.z - footprint_z)
|
||||
return tool.Cad.is_x(v.co.z - footprint_z, 0)
|
||||
|
||||
def is_footprint_edge(edge):
|
||||
return all(is_footprint_vert(v) for v in edge.verts)
|
||||
@@ -326,7 +322,7 @@ def generate_hiped_roof_bmesh(
|
||||
default_offset_dir = Vector([0, 0, 1]) * roof_thickness
|
||||
footprint_verts = set()
|
||||
|
||||
if not float_is_zero(rafter_edge_angle):
|
||||
if not tool.Cad.is_x(rafter_edge_angle, 0):
|
||||
footprint_edges = []
|
||||
for edge in extruded_edges:
|
||||
if is_footprint_edge(edge):
|
||||
@@ -398,7 +394,7 @@ def update_roof_modifier_ifc_data(context):
|
||||
if not angle_layer:
|
||||
return False
|
||||
for edge_angle in angle_layer:
|
||||
if float_is_zero(edge_angle - pi / 2):
|
||||
if tool.Cad.is_x(edge_angle - pi / 2, 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -95,19 +95,19 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
|
||||
gross_settings.set(gross_settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
|
||||
for obj in bpy.context.visible_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
visible_element = tool.Ifc.get_entity(obj)
|
||||
|
||||
if (
|
||||
not element
|
||||
not visible_element
|
||||
or obj.type != "MESH"
|
||||
or not self.is_bounding_class(element)
|
||||
or not self.is_bounding_class(visible_element)
|
||||
or not tool.Drawing.is_intersecting_plane(obj, self.cut_point, self.cut_normal)
|
||||
):
|
||||
continue
|
||||
|
||||
old_mesh = None
|
||||
if element.HasOpenings:
|
||||
new_mesh = self.create_mesh(ifcopenshell.geom.create_shape(gross_settings, element))
|
||||
if visible_element.HasOpenings:
|
||||
new_mesh = self.create_mesh(ifcopenshell.geom.create_shape(gross_settings, visible_element))
|
||||
old_mesh = obj.data
|
||||
obj.data = new_mesh
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ classes = (
|
||||
operator.SaveLibraryFile,
|
||||
operator.SelectLibraryFile,
|
||||
operator.ToggleFilterCategories,
|
||||
operator.ToggleLinkSelectability,
|
||||
operator.ToggleLinkVisibility,
|
||||
operator.UnassignLibraryDeclaration,
|
||||
operator.UnlinkIfc,
|
||||
|
||||
@@ -880,6 +880,27 @@ class LoadLink(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ToggleLinkSelectability(bpy.types.Operator):
|
||||
bl_idname = "bim.toggle_link_selectability"
|
||||
bl_label = "Toggle Link Selectability"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Toggle selectability"
|
||||
link: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMProjectProperties
|
||||
link = props.links.get(self.link)
|
||||
for collection in self.get_linked_collections():
|
||||
collection.hide_select = not collection.hide_select
|
||||
link.is_selectable = not collection.hide_select
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_linked_collections(self):
|
||||
return [
|
||||
c for c in bpy.data.collections if "IfcProject" in c.name and c.library and c.library.filepath == self.link
|
||||
]
|
||||
|
||||
|
||||
class ToggleLinkVisibility(bpy.types.Operator):
|
||||
bl_idname = "bim.toggle_link_visibility"
|
||||
bl_label = "Toggle Link Visibility"
|
||||
|
||||
@@ -93,6 +93,7 @@ class FilterCategory(PropertyGroup):
|
||||
class Link(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
is_loaded: BoolProperty(name="Is Loaded", default=False)
|
||||
is_selectable: BoolProperty(name="Is Selectable", default=True)
|
||||
is_wireframe: BoolProperty(name="Is Wireframe", default=False)
|
||||
is_hidden: BoolProperty(name="Is Hidden", default=False)
|
||||
|
||||
|
||||
@@ -350,6 +350,13 @@ class BIM_UL_links(UIList):
|
||||
row = layout.row(align=True)
|
||||
if item.is_loaded:
|
||||
row.label(text=item.name)
|
||||
op = row.operator(
|
||||
"bim.toggle_link_selectability",
|
||||
text="",
|
||||
icon="RESTRICT_SELECT_OFF" if item.is_selectable else "RESTRICT_SELECT_ON",
|
||||
emboss=False,
|
||||
)
|
||||
op.link = item.name
|
||||
op = row.operator(
|
||||
"bim.toggle_link_visibility",
|
||||
text="",
|
||||
|
||||
@@ -20,20 +20,20 @@ import blenderbim.bim.helper
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.resource.data import ResourceData
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
class BIM_PT_resources(Panel):
|
||||
bl_label = "Resources"
|
||||
bl_idname = "BIM_PT_resources"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_4D5D"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get() and tool.Ifc.get().schema != "IFC2X3"
|
||||
file = IfcStore.get_file()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMResourceProperties
|
||||
|
||||
@@ -20,12 +20,11 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.ExpandAllTasks,
|
||||
operator.ContractAllTasks,
|
||||
operator.AddAnimationCamera,
|
||||
operator.AddSummaryTask,
|
||||
operator.AddTask,
|
||||
operator.AddTaskColumn,
|
||||
operator.AddTaskBars,
|
||||
operator.AddTaskColumn,
|
||||
operator.AddTimePeriod,
|
||||
operator.AddWorkCalendar,
|
||||
operator.AddWorkPlan,
|
||||
@@ -42,11 +41,15 @@ classes = (
|
||||
operator.BlenderBIM_DatePickerSetDate,
|
||||
operator.BlenderBIM_RedrawDatePicker,
|
||||
operator.CalculateTaskDuration,
|
||||
operator.ClearPreviousAnimation,
|
||||
operator.ContractAllTasks,
|
||||
operator.ContractTask,
|
||||
operator.CopyTaskAttribute,
|
||||
operator.CopyTask,
|
||||
operator.CopyTaskAttribute,
|
||||
operator.CreateBaseline,
|
||||
operator.DisableEditingSequence,
|
||||
operator.DisableEditingTask,
|
||||
operator.DisableEditingTaskAnimationColors,
|
||||
operator.DisableEditingTaskTime,
|
||||
operator.DisableEditingWorkCalendar,
|
||||
operator.DisableEditingWorkPlan,
|
||||
@@ -67,23 +70,27 @@ classes = (
|
||||
operator.EnableEditingTaskCalendar,
|
||||
operator.EnableEditingTaskSequence,
|
||||
operator.EnableEditingTaskTime,
|
||||
operator.EnableEditingWorkScheduleTasks,
|
||||
operator.EnableEditingWorkCalendar,
|
||||
operator.EnableEditingWorkCalendarTimes,
|
||||
operator.EnableEditingWorkPlan,
|
||||
operator.EnableEditingWorkPlanSchedules,
|
||||
operator.EnableEditingWorkSchedule,
|
||||
operator.EnableEditingWorkScheduleTasks,
|
||||
operator.EnableEditingWorkTime,
|
||||
operator.ExpandAllTasks,
|
||||
operator.ExpandTask,
|
||||
operator.ExportMSP,
|
||||
operator.ExportP6,
|
||||
operator.GenerateGanttChart,
|
||||
operator.GuessDateRange,
|
||||
operator.ImportMSP,
|
||||
operator.HighlightTask,
|
||||
operator.ImportCSV,
|
||||
operator.ImportMSP,
|
||||
operator.ImportP6,
|
||||
operator.ImportP6XER,
|
||||
operator.ImportPP,
|
||||
operator.LoadProductTasks,
|
||||
operator.LoadTaskAnimationColors,
|
||||
operator.LoadTaskInputs,
|
||||
operator.LoadTaskOutputs,
|
||||
operator.LoadTaskProperties,
|
||||
@@ -98,10 +105,10 @@ classes = (
|
||||
operator.RemoveWorkSchedule,
|
||||
operator.RemoveWorkTime,
|
||||
operator.ReorderTask,
|
||||
operator.SelectTaskRelatedProducts,
|
||||
operator.SelectTaskRelatedInputs,
|
||||
operator.SelectWorkScheduleProducts,
|
||||
operator.SelectTaskRelatedProducts,
|
||||
operator.SelectUnassignedWorkScheduleProducts,
|
||||
operator.SelectWorkScheduleProducts,
|
||||
operator.SetTaskSortColumn,
|
||||
operator.SetupDefaultTaskColumns,
|
||||
operator.UnassignLagTime,
|
||||
@@ -113,11 +120,6 @@ classes = (
|
||||
operator.UnassignWorkSchedule,
|
||||
operator.VisualiseWorkScheduleDate,
|
||||
operator.VisualiseWorkScheduleDateRange,
|
||||
operator.LoadTaskAnimationColors,
|
||||
operator.DisableEditingTaskAnimationColors,
|
||||
operator.LoadProductTasks,
|
||||
operator.HighlightTask,
|
||||
operator.CreateBaseline,
|
||||
prop.WorkPlan,
|
||||
prop.BIMWorkPlanProperties,
|
||||
prop.Task,
|
||||
@@ -137,6 +139,7 @@ classes = (
|
||||
ui.BIM_PT_work_schedules,
|
||||
ui.BIM_PT_work_calendars,
|
||||
ui.BIM_PT_task_icom,
|
||||
ui.BIM_PT_animation_tools,
|
||||
ui.BIM_UL_task_columns,
|
||||
ui.BIM_UL_task_inputs,
|
||||
ui.BIM_UL_task_resources,
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell
|
||||
from ifcopenshell.util.doc import get_predefined_type_doc
|
||||
import ifcopenshell.util.date as dateutil
|
||||
|
||||
|
||||
@@ -37,7 +36,6 @@ class SequenceData:
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"predefined_types": cls.get_work_schedule_types(),
|
||||
"has_work_plans": cls.has_work_plans(),
|
||||
"has_work_schedules": cls.has_work_schedules(),
|
||||
"has_work_calendars": cls.has_work_calendars(),
|
||||
@@ -230,22 +228,6 @@ class SequenceData:
|
||||
data["NestingIndex"] = rel.RelatedObjects.index(task)
|
||||
cls.data["tasks"][task.id()] = data
|
||||
|
||||
@classmethod
|
||||
def get_work_schedule_types(cls):
|
||||
results = []
|
||||
declaration = tool.Ifc().schema().declaration_by_name("IfcWorkSchedule")
|
||||
version = tool.Ifc.get_schema()
|
||||
for attribute in declaration.attributes():
|
||||
if attribute.name() == "PredefinedType":
|
||||
results.extend(
|
||||
[
|
||||
(e, e, get_predefined_type_doc(version, "IfcWorkSchedule", e))
|
||||
for e in attribute.type_of_attribute().declared_type().enumeration_items()
|
||||
]
|
||||
)
|
||||
break
|
||||
return results
|
||||
|
||||
|
||||
class WorkScheduleData:
|
||||
data = {}
|
||||
|
||||
@@ -129,9 +129,21 @@ class AddWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_work_schedule"
|
||||
bl_label = "Add Work Schedule"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_work_schedule(tool.Ifc)
|
||||
core.add_work_schedule(tool.Ifc, tool.Sequence, name=self.name)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.prop(self, "name", text="Name")
|
||||
self.props = context.scene.BIMWorkScheduleProperties
|
||||
layout.prop(self.props, "work_schedule_predefined_types", text="Type")
|
||||
if self.props.work_schedule_predefined_types == "USERDEFINED":
|
||||
layout.prop(self.props,"object_type", text="Object type")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
class EditWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -1389,3 +1401,22 @@ class CreateBaseline(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
class ClearPreviousAnimation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.clear_previous_animation"
|
||||
bl_label = "Clear Previous Animation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
core.clear_previous_animation(tool.Sequence)
|
||||
|
||||
|
||||
class AddAnimationCamera(bpy.types.Operator):
|
||||
bl_idname = "bim.add_animation_camera"
|
||||
bl_label = "Add Camera to Scene"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
core.add_animation_camera(tool.Sequence)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import isodate
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.attribute
|
||||
from ifcopenshell.util.doc import get_predefined_type_doc
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.sequence as core
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
@@ -208,26 +209,31 @@ def updateTaskDuration(self, context):
|
||||
self.duration = "-"
|
||||
return
|
||||
|
||||
self.file = tool.Ifc.get()
|
||||
task = self.file.by_id(self.ifc_definition_id)
|
||||
task = tool.Ifc.get().by_id(self.ifc_definition_id)
|
||||
if task.TaskTime:
|
||||
task_time = task.TaskTime
|
||||
else:
|
||||
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=task)
|
||||
ifcopenshell.api.run(
|
||||
"sequence.edit_task_time",
|
||||
self.file,
|
||||
**{"task_time": task_time, "attributes": {"ScheduleDuration": duration}},
|
||||
)
|
||||
task_time = tool.Ifc.run("sequence.add_task_time", task=task)
|
||||
tool.Ifc.run("sequence.edit_task_time", task_time=task_time, attributes={"ScheduleDuration": duration})
|
||||
SequenceData.load()
|
||||
bpy.ops.bim.load_task_properties()
|
||||
|
||||
|
||||
def get_schedule_predefined_types(self, context):
|
||||
if not SequenceData.is_loaded:
|
||||
SequenceData.load()
|
||||
return SequenceData.data["predefined_types"]
|
||||
|
||||
results = []
|
||||
declaration = tool.Ifc().schema().declaration_by_name("IfcWorkSchedule")
|
||||
version = tool.Ifc.get_schema()
|
||||
for attribute in declaration.attributes():
|
||||
if attribute.name() == "PredefinedType":
|
||||
results.extend(
|
||||
[
|
||||
(e, e, get_predefined_type_doc(version, "IfcWorkSchedule", e))
|
||||
for e in attribute.type_of_attribute().declared_type().enumeration_items()
|
||||
if e != "BASELINE"
|
||||
]
|
||||
)
|
||||
break
|
||||
return results
|
||||
|
||||
def update_visualisation_start(self, context):
|
||||
update_visualisation_start_finish(self, context, "visualisation_start")
|
||||
@@ -292,6 +298,14 @@ def update_filter_by_active_schedule(self, context):
|
||||
tool.Sequence, product=tool.Ifc.get().by_id(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
)
|
||||
|
||||
def switch_options(self, context):
|
||||
if self.should_show_visualisation_ui:
|
||||
self.should_show_snapshot_ui = False
|
||||
|
||||
def switch_options2(self, context):
|
||||
if self.should_show_snapshot_ui:
|
||||
self.should_show_visualisation_ui = False
|
||||
|
||||
class Task(PropertyGroup):
|
||||
name: StringProperty(name="Name", update=updateTaskName)
|
||||
identification: StringProperty(name="Identification", update=updateTaskIdentification)
|
||||
@@ -352,6 +366,7 @@ class BIMWorkScheduleProperties(PropertyGroup):
|
||||
work_schedule_predefined_types: EnumProperty(
|
||||
items=get_schedule_predefined_types, name="Predefined Type", default=None
|
||||
)
|
||||
object_type: StringProperty(name="Object Type")
|
||||
durations_attributes: CollectionProperty(name="Durations Attributes", type=ISODuration)
|
||||
work_calendars: EnumProperty(items=getWorkCalendars, name="Work Calendars")
|
||||
work_schedule_attributes: CollectionProperty(name="Work Schedule Attributes", type=Attribute)
|
||||
@@ -362,9 +377,9 @@ class BIMWorkScheduleProperties(PropertyGroup):
|
||||
active_task_index: IntProperty(name="Active Task Index", update=update_active_task_index)
|
||||
active_task_id: IntProperty(name="Active Task Id")
|
||||
task_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
|
||||
should_show_visualisation_ui: BoolProperty(name="Should Show Visualisation UI", default=False)
|
||||
should_show_visualisation_ui: BoolProperty(name="Should Show Visualisation UI", default=True, update=switch_options)
|
||||
should_show_task_bar_selection: BoolProperty(name="Add to task bar", default=False)
|
||||
should_show_snapshot_ui: BoolProperty(name="Should Show Snapshot UI", default=False)
|
||||
should_show_snapshot_ui: BoolProperty(name="Should Show Snapshot UI", default=False, update=switch_options2)
|
||||
should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False)
|
||||
columns: CollectionProperty(name="Columns", type=Attribute)
|
||||
active_column_index: IntProperty(name="Active Column Index")
|
||||
@@ -397,9 +412,9 @@ class BIMWorkScheduleProperties(PropertyGroup):
|
||||
visualisation_start: StringProperty(name="Visualisation Start", update=update_visualisation_start)
|
||||
visualisation_finish: StringProperty(name="Visualisation Finish", update=update_visualisation_finish)
|
||||
speed_multiplier: FloatProperty(name="Speed Multiplier", default=10000)
|
||||
speed_animation_duration: StringProperty(name="Speed Animation Duration", default="PT1S")
|
||||
speed_animation_duration: StringProperty(name="Speed Animation Duration", default="1 s")
|
||||
speed_animation_frames: IntProperty(name="Speed Animation Frames", default=24)
|
||||
speed_real_duration: StringProperty(name="Speed Real Duration", default="P1W")
|
||||
speed_real_duration: StringProperty(name="Speed Real Duration", default="1 w")
|
||||
speed_types: EnumProperty(
|
||||
items=[
|
||||
("FRAME_SPEED", "Frame-based", "e.g. 25 frames = 1 real week"),
|
||||
|
||||
@@ -22,7 +22,6 @@ from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.helper import draw_attributes
|
||||
from blenderbim.bim.module.sequence.data import WorkPlansData, WorkScheduleData, SequenceData, TaskICOMData
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
class BIM_PT_work_plans(Panel):
|
||||
@@ -32,10 +31,12 @@ class BIM_PT_work_plans(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_4D5D"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get() and tool.Ifc.get().schema != "IFC2X3"
|
||||
file = IfcStore.get_file()
|
||||
return file and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
if not WorkPlansData.is_loaded:
|
||||
@@ -54,11 +55,10 @@ class BIM_PT_work_plans(Panel):
|
||||
def draw_work_plan_ui(self, work_plan):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=work_plan["name"], icon="TEXT")
|
||||
|
||||
if self.props.active_work_plan_id == work_plan["id"]:
|
||||
if self.props.editing_type == "ATTRIBUTES":
|
||||
row.operator("bim.edit_work_plan", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_work_plan", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_editing_work_plan", text="Cancel", icon="CANCEL")
|
||||
elif self.props.active_work_plan_id:
|
||||
row.operator("bim.remove_work_plan", text="", icon="X").work_plan = work_plan["id"]
|
||||
else:
|
||||
@@ -101,10 +101,12 @@ class BIM_PT_work_schedules(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_4D5D"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get() and tool.Ifc.get().schema != "IFC2X3"
|
||||
file = IfcStore.get_file()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
if not SequenceData.is_loaded:
|
||||
@@ -113,10 +115,9 @@ class BIM_PT_work_schedules(Panel):
|
||||
WorkScheduleData.load()
|
||||
self.props = context.scene.BIMWorkScheduleProperties
|
||||
self.tprops = context.scene.BIMTaskTreeProperties
|
||||
self.animation_props = context.scene.BIMAnimationProperties
|
||||
|
||||
if not self.props.active_work_schedule_id:
|
||||
row = self.layout.row()
|
||||
row = self.layout.row(align=True)
|
||||
if SequenceData.data["has_work_schedules"]:
|
||||
row.label(
|
||||
text="{} Work Schedules Found".format(SequenceData.data["number_of_work_schedules_loaded"]),
|
||||
@@ -124,25 +125,25 @@ class BIM_PT_work_schedules(Panel):
|
||||
)
|
||||
else:
|
||||
row.label(text="No Work Schedules found.", icon="TEXT")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(self.props, "work_schedule_predefined_types")
|
||||
row.operator("bim.add_work_schedule", text="Add", icon="ADD")
|
||||
|
||||
for work_schedule_id, work_schedule in SequenceData.data["work_schedules"].items():
|
||||
|
||||
self.draw_work_schedule_ui(work_schedule_id, work_schedule)
|
||||
|
||||
def draw_work_schedule_ui(self, work_schedule_id, work_schedule):
|
||||
if not work_schedule["PredefinedType"] == "BASELINE":
|
||||
if work_schedule["PredefinedType"] == "BASELINE":
|
||||
self.draw_readonly_work_schedule_ui(work_schedule_id)
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=work_schedule["Name"] or "Unnamed", icon="LINENUMBERS_ON")
|
||||
if self.props.active_work_schedule_id == work_schedule_id:
|
||||
row.label(
|
||||
text="Currently editing: {}[{}]".format(work_schedule["Name"], work_schedule["PredefinedType"]),
|
||||
icon="LINENUMBERS_ON",
|
||||
)
|
||||
if self.props.editing_type == "WORK_SCHEDULE":
|
||||
row.operator("bim.edit_work_schedule", text="", icon="CHECKMARK")
|
||||
row.operator("bim.edit_work_schedule", text="Apply", icon="CHECKMARK")
|
||||
elif self.props.editing_type == "TASKS":
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.alignment = "LEFT"
|
||||
@@ -177,33 +178,23 @@ class BIM_PT_work_schedules(Panel):
|
||||
row1.alignment = "RIGHT"
|
||||
row1.prop(self.props, "should_show_column_ui", text="Schedule Columns", icon="SHORTDISPLAY")
|
||||
row2 = col.row(align=True)
|
||||
row2.prop(
|
||||
self.props, "should_show_visualisation_ui", text="Animation Options", icon="CAMERA_STEREO"
|
||||
)
|
||||
row2.prop(self.props, "should_show_snapshot_ui", text="Snapshot Options", icon="CAMERA_STEREO")
|
||||
row.operator("bim.disable_editing_work_schedule", text="Disable editing", icon="CANCEL")
|
||||
else:
|
||||
row.operator("bim.disable_editing_work_schedule", text="Cancel", icon="CANCEL")
|
||||
if not self.props.active_work_schedule_id:
|
||||
row.label(text="{}[{}]".format(work_schedule["Name"], work_schedule["PredefinedType"]) or "Unnamed", icon="LINENUMBERS_ON")
|
||||
row.operator(
|
||||
"bim.enable_editing_work_schedule_tasks", text="", icon="ACTION"
|
||||
"bim.enable_editing_work_schedule_tasks", text="Tasks", icon="ACTION"
|
||||
).work_schedule = work_schedule_id
|
||||
row.operator(
|
||||
"bim.enable_editing_work_schedule", text="", icon="GREASEPENCIL"
|
||||
"bim.enable_editing_work_schedule", text="Attributes", icon="GREASEPENCIL"
|
||||
).work_schedule = work_schedule_id
|
||||
row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = work_schedule_id
|
||||
|
||||
row.operator("bim.remove_work_schedule", text="Delete", icon="X").work_schedule = work_schedule_id
|
||||
if self.props.active_work_schedule_id == work_schedule_id:
|
||||
if self.props.editing_type == "WORK_SCHEDULE":
|
||||
self.draw_editable_work_schedule_ui()
|
||||
elif self.props.editing_type == "TASKS":
|
||||
self.draw_baseline_ui(work_schedule_id)
|
||||
self.draw_column_ui()
|
||||
if self.props.should_show_visualisation_ui:
|
||||
self.draw_visualisation_ui()
|
||||
if self.props.should_show_snapshot_ui:
|
||||
self.draw_snapshot_ui()
|
||||
self.draw_editable_task_ui(work_schedule_id)
|
||||
else:
|
||||
self.draw_readonly_work_schedule_ui(work_schedule_id)
|
||||
|
||||
def draw_task_operators(self):
|
||||
row = self.layout.row(align=True)
|
||||
@@ -218,7 +209,7 @@ class BIM_PT_work_schedules(Panel):
|
||||
row.operator("bim.edit_task_time", text="", icon="CHECKMARK")
|
||||
elif self.props.editing_task_type == "ATTRIBUTES":
|
||||
row.operator("bim.edit_task", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_task", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_editing_task", text="Cancel", icon="CANCEL")
|
||||
else:
|
||||
row.prop(self.props, "show_task_operators", text="Edit", icon="GREASEPENCIL")
|
||||
if self.props.show_task_operators:
|
||||
@@ -242,7 +233,7 @@ class BIM_PT_work_schedules(Panel):
|
||||
if not self.props.should_show_column_ui:
|
||||
return
|
||||
row = self.layout.row()
|
||||
row.operator("bim.setup_default_task_columns", text="Add Default Columns", icon="ANCHOR_BOTTOM")
|
||||
row.operator("bim.setup_default_task_columns", text="Setup Default Columns", icon="ANCHOR_BOTTOM")
|
||||
row.alignment = "RIGHT"
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "column_types", text="")
|
||||
@@ -268,103 +259,6 @@ class BIM_PT_work_schedules(Panel):
|
||||
|
||||
self.layout.template_list("BIM_UL_task_columns", "", self.props, "columns", self.props, "active_column_index")
|
||||
|
||||
def draw_visualisation_ui(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Start Date/ Date Range:")
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.datepicker", text=self.props.visualisation_start or "Start Date", icon="REW")
|
||||
op.target_prop = "BIMWorkScheduleProperties.visualisation_start"
|
||||
op = row.operator("bim.datepicker", text=self.props.visualisation_finish or "Finish Date", icon="FF")
|
||||
op.target_prop = "BIMWorkScheduleProperties.visualisation_finish"
|
||||
op = row.operator("bim.guess_date_range", text="Guess", icon="FILE_REFRESH")
|
||||
op.work_schedule = self.props.active_work_schedule_id
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Speed Settings")
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "speed_types", text="")
|
||||
if self.props.speed_types == "FRAME_SPEED":
|
||||
row.prop(self.props, "speed_animation_frames", text="")
|
||||
row.prop(self.props, "speed_real_duration", text="")
|
||||
elif self.props.speed_types == "DURATION_SPEED":
|
||||
row.prop(self.props, "speed_animation_duration", text="")
|
||||
row.prop(self.props, "speed_real_duration", text="")
|
||||
elif self.props.speed_types == "MULTIPLIER_SPEED":
|
||||
row.prop(self.props, "speed_multiplier", text="")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Display Settings")
|
||||
row = self.layout.row(align=True)
|
||||
if not self.animation_props.is_editing:
|
||||
op = row.operator(
|
||||
"bim.enable_editing_task_animation_colors", text="Customize Object Colors", icon="SEQUENCE_COLOR_04"
|
||||
)
|
||||
else:
|
||||
op = row.operator(
|
||||
"bim.disable_editing_task_animation_colors", text="Hide Object Colors", icon="SEQUENCE_COLOR_01"
|
||||
)
|
||||
|
||||
row.prop(self.animation_props, "should_show_task_bar_options", text="Task Bar", icon="NLA_PUSHDOWN")
|
||||
if self.animation_props.should_show_task_bar_options:
|
||||
row = self.layout.row()
|
||||
row.label(text="Task Bar Options", icon="NLA_PUSHDOWN")
|
||||
row.alignment = "LEFT"
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "should_show_task_bar_selection", text="Enable Selection", icon="NLA_PUSHDOWN")
|
||||
row.operator("bim.add_task_bars", text="Generate bars", icon="NLA_PUSHDOWN")
|
||||
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
# Column1
|
||||
col = grid.column()
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(self.animation_props, "color_progress")
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(self.animation_props, "color_full")
|
||||
|
||||
if self.animation_props.is_editing:
|
||||
self.draw_visualisation_settings_ui()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.visualise_work_schedule_date_range", text="Create Animation", icon="OUTLINER_OB_CAMERA")
|
||||
op.work_schedule = self.props.active_work_schedule_id
|
||||
|
||||
def draw_snapshot_ui(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Create Construction Snapshot:")
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.datepicker", text=self.props.visualisation_start or "Date", icon="REW")
|
||||
op.target_prop = "BIMWorkScheduleProperties.visualisation_start"
|
||||
op = row.operator("bim.visualise_work_schedule_date", text="Create SnapShot", icon="RESTRICT_RENDER_OFF")
|
||||
op.work_schedule = self.props.active_work_schedule_id
|
||||
|
||||
def draw_visualisation_settings_ui(self):
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.label(text="INPUT COLORS", icon="COLLECTION_COLOR_01")
|
||||
row1 = col.row()
|
||||
row1.template_list(
|
||||
"BIM_UL_animation_colors",
|
||||
"",
|
||||
self.animation_props,
|
||||
"task_colors_components_inputs",
|
||||
self.animation_props,
|
||||
"active_color_component_inputs_index",
|
||||
)
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.label(text="OUTPUT COLORS", icon="COLLECTION_COLOR_04")
|
||||
row1 = col.row()
|
||||
row1.template_list(
|
||||
"BIM_UL_animation_colors",
|
||||
"",
|
||||
self.animation_props,
|
||||
"task_colors_components_outputs",
|
||||
self.animation_props,
|
||||
"active_color_component_outputs_index",
|
||||
)
|
||||
|
||||
def draw_editable_work_schedule_ui(self):
|
||||
draw_attributes(self.props.work_schedule_attributes, self.layout)
|
||||
|
||||
@@ -421,12 +315,12 @@ class BIM_PT_work_schedules(Panel):
|
||||
if self.props.active_sequence_id == sequence["id"]:
|
||||
if self.props.editing_sequence_type == "ATTRIBUTES":
|
||||
row.operator("bim.edit_sequence_attributes", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_sequence", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_editing_sequence", text="Cancel", icon="CANCEL")
|
||||
self.draw_editable_sequence_attributes_ui()
|
||||
elif self.props.editing_sequence_type == "LAG_TIME":
|
||||
op = row.operator("bim.edit_sequence_lag_time", text="", icon="CHECKMARK")
|
||||
op.lag_time = sequence["TimeLag"]
|
||||
row.operator("bim.disable_editing_sequence", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_editing_sequence", text="Cancel", icon="CANCEL")
|
||||
self.draw_editable_sequence_lag_time_ui()
|
||||
else:
|
||||
if sequence["TimeLag"]:
|
||||
@@ -491,7 +385,7 @@ class BIM_PT_work_schedules(Panel):
|
||||
"id"
|
||||
]
|
||||
baseline_row.operator(
|
||||
"bim.enable_editing_work_schedule_tasks", text="", icon="ACTION"
|
||||
"bim.enable_editing_work_schedule_tasks", text="Display Schedule", icon="ACTION"
|
||||
).work_schedule = baseline["id"]
|
||||
baseline_row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = baseline["id"]
|
||||
|
||||
@@ -521,6 +415,151 @@ class BIM_PT_work_schedules(Panel):
|
||||
)
|
||||
|
||||
|
||||
class BIM_PT_animation_tools(Panel):
|
||||
bl_label = "Animation Tools"
|
||||
bl_idname = "BIM_PT_animation_tools"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_work_schedules"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BIMWorkScheduleProperties
|
||||
if props.active_work_schedule_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMWorkScheduleProperties
|
||||
self.animation_props = context.scene.BIMAnimationProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(
|
||||
self.props, "should_show_visualisation_ui", text="Animation Settings", icon="SETTINGS"
|
||||
)
|
||||
row.prop(self.props, "should_show_snapshot_ui", text="Snapshot Settings", icon="SETTINGS")
|
||||
if self.props.should_show_visualisation_ui:
|
||||
self.draw_visualisation_ui()
|
||||
if self.props.should_show_snapshot_ui:
|
||||
self.draw_snapshot_ui()
|
||||
self.draw_processing_options()
|
||||
|
||||
|
||||
def draw_processing_options(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "LEFT"
|
||||
row.label(text="Processing Tools")
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.clear_previous_animation", text="Clear Previous Animation", icon="TRACKING_CLEAR_FORWARDS")
|
||||
row.operator("bim.add_animation_camera", text="Add Camera", icon="CAMERA_DATA")
|
||||
|
||||
def draw_visualisation_ui(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Start Date/ Date Range:", icon="CAMERA_DATA")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
op = row.operator("bim.datepicker", text=self.props.visualisation_start or "Start Date", icon="REW")
|
||||
op.target_prop = "BIMWorkScheduleProperties.visualisation_start"
|
||||
op = row.operator("bim.datepicker", text=self.props.visualisation_finish or "Finish Date", icon="FF")
|
||||
op.target_prop = "BIMWorkScheduleProperties.visualisation_finish"
|
||||
op = row.operator("bim.guess_date_range", text="Guess", icon="FILE_REFRESH")
|
||||
op.work_schedule = self.props.active_work_schedule_id
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Speed Settings")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(self.props, "speed_types", text="")
|
||||
if self.props.speed_types == "FRAME_SPEED":
|
||||
row.prop(self.props, "speed_animation_frames", text="")
|
||||
row.prop(self.props, "speed_real_duration", text="")
|
||||
elif self.props.speed_types == "DURATION_SPEED":
|
||||
row.prop(self.props, "speed_animation_duration", text="")
|
||||
row.label(text="->")
|
||||
row.prop(self.props, "speed_real_duration", text="")
|
||||
elif self.props.speed_types == "MULTIPLIER_SPEED":
|
||||
row.prop(self.props, "speed_multiplier", text="")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Display Settings")
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
if not self.animation_props.is_editing:
|
||||
op = row.operator(
|
||||
"bim.enable_editing_task_animation_colors", text="Customize Object Colors", icon="SEQUENCE_COLOR_04"
|
||||
)
|
||||
else:
|
||||
op = row.operator(
|
||||
"bim.disable_editing_task_animation_colors", text="Hide Object Colors", icon="SEQUENCE_COLOR_01"
|
||||
)
|
||||
|
||||
row.prop(self.animation_props, "should_show_task_bar_options", text="Task Bar", icon="NLA_PUSHDOWN")
|
||||
if self.animation_props.should_show_task_bar_options:
|
||||
row = self.layout.row()
|
||||
row.label(text="Task Bar Options", icon="NLA_PUSHDOWN")
|
||||
row.alignment = "LEFT"
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "should_show_task_bar_selection", text="Enable Selection", icon="NLA_PUSHDOWN")
|
||||
row.operator("bim.add_task_bars", text="Generate bars", icon="NLA_PUSHDOWN")
|
||||
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
# Column1
|
||||
col = grid.column()
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(self.animation_props, "color_progress")
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(self.animation_props, "color_full")
|
||||
|
||||
if self.animation_props.is_editing:
|
||||
self.draw_visualisation_settings_ui()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.visualise_work_schedule_date_range", text="Create Animation", icon="OUTLINER_OB_CAMERA")
|
||||
op.work_schedule = self.props.active_work_schedule_id
|
||||
|
||||
def draw_snapshot_ui(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Date of Snapshot:", icon="CAMERA_STEREO")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
op = row.operator("bim.datepicker", text=self.props.visualisation_start or "Date", icon="PROP_PROJECTED")
|
||||
op.target_prop = "BIMWorkScheduleProperties.visualisation_start"
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
op = row.operator("bim.visualise_work_schedule_date", text="Create SnapShot", icon="CAMERA_STEREO")
|
||||
op.work_schedule = self.props.active_work_schedule_id
|
||||
|
||||
def draw_visualisation_settings_ui(self):
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.label(text="INPUT COLORS", icon="COLLECTION_COLOR_01")
|
||||
row1 = col.row()
|
||||
row1.template_list(
|
||||
"BIM_UL_animation_colors",
|
||||
"",
|
||||
self.animation_props,
|
||||
"task_colors_components_inputs",
|
||||
self.animation_props,
|
||||
"active_color_component_inputs_index",
|
||||
)
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.label(text="OUTPUT COLORS", icon="COLLECTION_COLOR_04")
|
||||
row1 = col.row()
|
||||
row1.template_list(
|
||||
"BIM_UL_animation_colors",
|
||||
"",
|
||||
self.animation_props,
|
||||
"task_colors_components_outputs",
|
||||
self.animation_props,
|
||||
"active_color_component_outputs_index",
|
||||
)
|
||||
|
||||
class BIM_PT_task_icom(Panel):
|
||||
bl_label = "Task ICOM"
|
||||
bl_idname = "BIM_PT_task_icom"
|
||||
@@ -813,10 +852,12 @@ class BIM_PT_work_calendars(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_4D5D"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get() and tool.Ifc.get().schema != "IFC2X3"
|
||||
file = IfcStore.get_file()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
if not SequenceData.is_loaded:
|
||||
@@ -841,7 +882,7 @@ class BIM_PT_work_calendars(Panel):
|
||||
if self.props.active_work_calendar_id == work_calendar_id:
|
||||
if self.props.editing_type == "ATTRIBUTES":
|
||||
row.operator("bim.edit_work_calendar", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_work_calendar", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_editing_work_calendar", text="Cancel", icon="CANCEL")
|
||||
elif self.props.active_work_calendar_id:
|
||||
row.operator("bim.remove_work_calendar", text="", icon="X").work_calendar = work_calendar_id
|
||||
else:
|
||||
@@ -879,7 +920,7 @@ class BIM_PT_work_calendars(Panel):
|
||||
row.label(text="{} - {}".format(work_time["Start"] or "*", work_time["Finish"] or "*"))
|
||||
if self.props.active_work_time_id == work_time["id"]:
|
||||
row.operator("bim.edit_work_time", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_work_time", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_editing_work_time", text="Cancel", icon="CANCEL")
|
||||
elif self.props.active_work_time_id:
|
||||
op = row.operator("bim.remove_work_time", text="", icon="X")
|
||||
op.work_time = work_time["id"]
|
||||
|
||||
@@ -34,6 +34,7 @@ classes = (
|
||||
operator.RemovePort,
|
||||
operator.RemoveSystem,
|
||||
operator.SelectSystemProducts,
|
||||
operator.MEPConnectElements,
|
||||
operator.SetFlowDirection,
|
||||
operator.ShowPorts,
|
||||
operator.UnassignSystem,
|
||||
|
||||
@@ -23,6 +23,7 @@ import blenderbim.core.system as core
|
||||
import blenderbim.bim.handler
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.system.data import PortData
|
||||
from mathutils import Matrix
|
||||
|
||||
|
||||
class Operator:
|
||||
@@ -215,6 +216,42 @@ class DisconnectPort(bpy.types.Operator, Operator):
|
||||
core.disconnect_port(tool.Ifc, port=element)
|
||||
|
||||
|
||||
class MEPConnectElements(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.mep_connect_elements"
|
||||
bl_label = "Connect MEP Elements"
|
||||
bl_description = "Connects two selected elements if they have ports with matching location"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return len(context.selected_objects) == 2
|
||||
|
||||
def _execute(self, context):
|
||||
obj1 = context.active_object
|
||||
obj2 = next(o for o in context.selected_objects if o != obj1)
|
||||
|
||||
el1 = tool.Ifc.get_entity(obj1)
|
||||
el2 = tool.Ifc.get_entity(obj2)
|
||||
|
||||
obj1_ports = [p for p in tool.System.get_ports(el1) if not tool.System.get_connected_port(p)]
|
||||
obj2_ports = [p for p in tool.System.get_ports(el2) if not tool.System.get_connected_port(p)]
|
||||
|
||||
if not obj1_ports or not obj2_ports:
|
||||
self.report({"ERROR"}, "Couldn't find free ports to connect.")
|
||||
return
|
||||
|
||||
for port1 in obj1_ports:
|
||||
port1_location = tool.Model.get_element_matrix(port1).translation
|
||||
for port2 in obj2_ports:
|
||||
port2_location = tool.Model.get_element_matrix(port2).translation
|
||||
if tool.Cad.are_vectors_equal(port1_location, port2_location):
|
||||
core.connect_port(tool.Ifc, port1, port2)
|
||||
return {"FINISHED"}
|
||||
|
||||
self.report({"ERROR"}, "Couldn't find any matching ports to connect.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
class SetFlowDirection(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.set_flow_direction"
|
||||
bl_label = "Set Flow Direction"
|
||||
|
||||
@@ -123,6 +123,7 @@ class BIM_PT_object_systems(Panel):
|
||||
"IfcDistributionSystem": "NETWORK_DRIVE",
|
||||
"IfcDistributionCircuit": "DRIVER",
|
||||
"IfcBuildingSystem": "MOD_BUILD",
|
||||
"IfcBuiltSystem": "MOD_BUILD",
|
||||
"IfcZone": "CUBE",
|
||||
}
|
||||
for system in ObjectSystemData.data["systems"]:
|
||||
@@ -166,6 +167,7 @@ class BIM_PT_ports(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
total_ports = PortData.data["total_ports"]
|
||||
row.label(text=f"{total_ports} Ports Found", icon="PLUGIN")
|
||||
row.operator("bim.mep_connect_elements", text="", 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="")
|
||||
@@ -283,6 +285,7 @@ class BIM_UL_systems(UIList):
|
||||
"IfcDistributionSystem": "NETWORK_DRIVE",
|
||||
"IfcDistributionCircuit": "DRIVER",
|
||||
"IfcBuildingSystem": "MOD_BUILD",
|
||||
"IfcBuiltSystem": "MOD_BUILD",
|
||||
"IfcZone": "CUBE",
|
||||
}
|
||||
if item:
|
||||
@@ -315,6 +318,7 @@ class BIM_UL_object_systems(UIList):
|
||||
"IfcDistributionSystem": "NETWORK_DRIVE",
|
||||
"IfcDistributionCircuit": "DRIVER",
|
||||
"IfcBuildingSystem": "MOD_BUILD",
|
||||
"IfcBuiltSystem": "MOD_BUILD",
|
||||
"IfcZone": "CUBE",
|
||||
}
|
||||
if item:
|
||||
|
||||
@@ -305,7 +305,7 @@ def get_tab(self, context):
|
||||
("DRAWINGS", "Drawings and Documents", "", "DOCUMENTS", 3),
|
||||
("SERVICES", "Services and Systems", "", "NETWORK_DRIVE", 4),
|
||||
("STRUCTURE", "Structural Analysis", "", "EDITMODE_HLT", 5),
|
||||
("SCHEDULING", "Construction Scheduling", "", "NLA", 6),
|
||||
("SCHEDULING", "Costing and Scheduling", "", "NLA", 6),
|
||||
("FM", "Facility Management", "", "PACKAGE", 7),
|
||||
("QUALITY", "Quality and Coordination", "", "COMMUNITY", 8),
|
||||
("BLENDER", "Blender Properties", "", "BLENDER", 9),
|
||||
|
||||
@@ -404,6 +404,21 @@ class BIM_PT_geometry(Panel):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_4D5D(Panel):
|
||||
bl_label = "Costing and Scheduling"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_structural(Panel):
|
||||
bl_label = "Structural"
|
||||
bl_space_type = "PROPERTIES"
|
||||
|
||||
@@ -51,6 +51,8 @@ def close_brick_project(brick):
|
||||
brick.clear_project()
|
||||
brick.clear_brick_browser()
|
||||
brick.clear_brick_browser(split_screen=True)
|
||||
brick.clear_breadcrumbs()
|
||||
brick.clear_breadcrumbs(split_screen=True)
|
||||
|
||||
|
||||
def convert_brick_project(ifc, brick):
|
||||
@@ -132,9 +134,7 @@ def add_namespace(brick, alias=None, uri=None):
|
||||
|
||||
|
||||
def set_brick_list_root(brick, brick_root=None, split_screen=False):
|
||||
brick.clear_brick_browser(split_screen=split_screen)
|
||||
brick.import_brick_classes(brick_root, split_screen=split_screen)
|
||||
brick.set_active_brick_class(brick_root, split_screen=split_screen)
|
||||
brick.run_view_brick_class(brick_class=brick_root, split_screen=split_screen)
|
||||
brick.clear_breadcrumbs(split_screen=split_screen)
|
||||
|
||||
|
||||
|
||||
@@ -345,11 +345,10 @@ def update_drawing_name(ifc, drawing_tool, drawing=None, name=None):
|
||||
|
||||
|
||||
def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None):
|
||||
context = drawing_tool.get_annotation_context(
|
||||
target_view := drawing_tool.get_drawing_target_view(drawing), object_type
|
||||
)
|
||||
target_view = drawing_tool.get_drawing_target_view(drawing)
|
||||
context = drawing_tool.get_annotation_context(target_view, object_type)
|
||||
if not context:
|
||||
return f"No annotation context Annotation/{target_view} for drawing"
|
||||
context = drawing_tool.create_annotation_context(target_view, object_type)
|
||||
|
||||
drawing_tool.show_decorations()
|
||||
obj = drawing_tool.create_annotation_object(drawing, object_type)
|
||||
|
||||
@@ -50,8 +50,9 @@ def enable_editing_work_plan_schedules(sequence, work_plan=None):
|
||||
sequence.enable_editing_work_plan_schedules(work_plan)
|
||||
|
||||
|
||||
def add_work_schedule(ifc):
|
||||
return ifc.run("sequence.add_work_schedule")
|
||||
def add_work_schedule(ifc, sequence, name=None):
|
||||
predefined_type, object_type = sequence.get_user_predefined_type()
|
||||
return ifc.run("sequence.add_work_schedule", name=name, predefined_type=predefined_type, object_type=object_type)
|
||||
|
||||
|
||||
def remove_work_schedule(ifc, work_schedule=None):
|
||||
@@ -529,6 +530,7 @@ def disable_editing_task_animation_colors(sequence):
|
||||
|
||||
|
||||
def visualise_work_schedule_date_range(sequence, work_schedule=None):
|
||||
sequence.clear_objects_animation(include_blender_objects=False)
|
||||
settings = sequence.get_animation_settings()
|
||||
if settings:
|
||||
product_frames = sequence.get_animation_product_frames(work_schedule, settings)
|
||||
@@ -579,3 +581,9 @@ def reorder_task_nesting(ifc, sequence, task, new_index):
|
||||
|
||||
def create_baseline(ifc, sequence, work_schedule, name):
|
||||
ifc.run("sequence.create_baseline", work_schedule=work_schedule, name=name)
|
||||
|
||||
def clear_previous_animation(sequence):
|
||||
sequence.clear_objects_animation(include_blender_objects=False)
|
||||
|
||||
def add_animation_camera(sequence):
|
||||
sequence.add_animation_camera()
|
||||
@@ -183,7 +183,6 @@ class Cost:
|
||||
def get_cost_value_unit_component(cls): pass
|
||||
def get_direct_cost_item_products(cls): pass
|
||||
def get_highlighted_cost_item(cls): pass
|
||||
def get_highlighted_cost_item(cls): pass
|
||||
def get_products(cls, related_object_type): pass
|
||||
def get_schedule_cost_items(cls, cost_schedule): pass
|
||||
def get_units(cls): pass
|
||||
@@ -648,6 +647,7 @@ class Search:
|
||||
|
||||
@interface
|
||||
class Sequence:
|
||||
def add_animation_camera(cls): pass
|
||||
def add_task_column(cls, column_type, name, data_type): pass
|
||||
def add_text_animation_handler(cls, settings): pass
|
||||
def animate_consumption(cls, obj, start_frame, product_frame, color, animation_type): pass
|
||||
@@ -660,6 +660,7 @@ class Sequence:
|
||||
def animate_operation(cls, obj, start_frame, product_frame, color): pass
|
||||
def animate_output(cls, obj, start_frame, product_frame): pass
|
||||
def clear_object_animation(cls, obj): pass
|
||||
def clear_object_color(cls, obj): pass
|
||||
def clear_objects_animation(cls, include_blender_objects): pass
|
||||
def contract_all_tasks(cls): pass
|
||||
def contract_task(cls, task): pass
|
||||
@@ -676,6 +677,7 @@ class Sequence:
|
||||
def disable_editing_work_time(cls): pass
|
||||
def disable_selecting_deleted_task(cls): pass
|
||||
def disable_work_schedule(cls): pass
|
||||
def display_object(cls, obj): pass
|
||||
def enable_editing_rel_sequence_attributes(cls, rel_sequence): pass
|
||||
def enable_editing_sequence_lag_time(cls, rel_sequence): pass
|
||||
def enable_editing_task_animation_colors(cls): pass
|
||||
@@ -719,6 +721,7 @@ class Sequence:
|
||||
def get_task_time_attributes(cls): pass
|
||||
def get_task_time(cls, task): pass
|
||||
def get_tasks_for_product(cls, product, work_schedule): pass
|
||||
def get_user_predefined_type(cls): pass
|
||||
def get_work_calendar_attributes(cls): pass
|
||||
def get_work_plan_attributes(cls): pass
|
||||
def get_work_schedule_attributes(cls): pass
|
||||
|
||||
@@ -23,6 +23,7 @@ import ifcopenshell.util.brick
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
from contextlib import contextmanager
|
||||
import datetime
|
||||
|
||||
try:
|
||||
import brickschema
|
||||
@@ -308,6 +309,8 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
with BrickStore.graph.new_changeset("PROJECT") as cs:
|
||||
cs.load_file(filepath)
|
||||
BrickStore.path = filepath
|
||||
cls.set_last_saved()
|
||||
BrickStore.load_sub_roots()
|
||||
BrickStore.load_namespaces()
|
||||
BrickStore.load_entity_classes()
|
||||
BrickStore.load_relationships()
|
||||
@@ -321,6 +324,7 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
with BrickStore.graph.new_changeset("SCHEMA") as cs:
|
||||
cs.load_file(BrickStore.schema)
|
||||
BrickStore.graph.bind("digitaltwin", Namespace("https://example.org/digitaltwin#"))
|
||||
BrickStore.load_sub_roots()
|
||||
BrickStore.load_namespaces()
|
||||
BrickStore.load_entity_classes()
|
||||
BrickStore.load_relationships()
|
||||
@@ -340,10 +344,9 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
|
||||
@classmethod
|
||||
def remove_brick(cls, brick_uri):
|
||||
if BrickStore.graph.triples((URIRef(brick_uri), None, None)):
|
||||
with BrickStore.new_changeset() as cs:
|
||||
for triple in BrickStore.graph.triples((URIRef(brick_uri), None, None)):
|
||||
cs.remove(triple)
|
||||
with BrickStore.new_changeset() as cs:
|
||||
for triple in BrickStore.graph.triples((URIRef(brick_uri), None, None)):
|
||||
cs.remove(triple)
|
||||
|
||||
@classmethod
|
||||
def run_assign_brick_reference(cls, element=None, library=None, brick_uri=None):
|
||||
@@ -379,6 +382,7 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
@classmethod
|
||||
def serialize_brick(cls):
|
||||
BrickStore.get_project().serialize(destination=BrickStore.path, format="turtle")
|
||||
cls.set_last_saved()
|
||||
|
||||
@classmethod
|
||||
def add_namespace(cls, alias, uri):
|
||||
@@ -392,22 +396,24 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
else:
|
||||
bpy.context.scene.BIMBrickProperties.brick_breadcrumbs.clear()
|
||||
|
||||
@classmethod
|
||||
def set_last_saved(cls):
|
||||
save = os.path.getmtime(BrickStore.path)
|
||||
save = datetime.datetime.fromtimestamp(save)
|
||||
BrickStore.last_saved = f"{save.year}-{save.month}-{save.day} {save.hour}:{save.minute}"
|
||||
|
||||
class BrickStore:
|
||||
schema = None # this is now a os path
|
||||
path = None # file path if the project was loaded in
|
||||
graph = None # this is the VersionedGraphCollection with 2 arbitrarily named graphs: "schema" and "project"
|
||||
# "SCHEMA" holds the Brick.ttl metadata; "PROJECT" holds all the authored entities
|
||||
last_saved = None
|
||||
history = []
|
||||
future = []
|
||||
current_changesets = 0
|
||||
history_size = 64
|
||||
namespaces = []
|
||||
root_classes = ["Equipment",
|
||||
"Electrical_Equipment", "Fire_Safety_Equipment", "HVAC_Equipment", "Lighting_Equipment", "Meter",
|
||||
"Location",
|
||||
"System",
|
||||
"Point",
|
||||
"Alarm", "Command", "Parameter", "Sensor", "Setpoint", "Status"]
|
||||
root_classes = ["Equipment", "Location", "System", "Point"]
|
||||
entity_classes = {}
|
||||
relationships = []
|
||||
|
||||
@@ -416,7 +422,9 @@ class BrickStore:
|
||||
BrickStore.schema = None
|
||||
BrickStore.graph = None
|
||||
BrickStore.path = None
|
||||
BrickStore.last_saved = None
|
||||
BrickStore.namespaces = []
|
||||
BrickStore.root_classes = ["Equipment", "Location", "System", "Point"]
|
||||
BrickStore.entity_classes = {}
|
||||
BrickStore.relationships = []
|
||||
|
||||
@@ -424,10 +432,36 @@ class BrickStore:
|
||||
def get_project(cls):
|
||||
return BrickStore.graph.graph_at(graph="PROJECT")
|
||||
|
||||
@classmethod
|
||||
def load_sub_roots(cls):
|
||||
query = BrickStore.graph.query(
|
||||
"""
|
||||
PREFIX brick: <https://brickschema.org/schema/Brick#>
|
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||
SELECT ?subRoot ?subClasses WHERE {
|
||||
{
|
||||
SELECT ?subRoot (COUNT(?subClass) as ?subClasses) WHERE {
|
||||
{
|
||||
?subRoot rdfs:subClassOf brick:Equipment .
|
||||
} UNION {
|
||||
?subRoot rdfs:subClassOf brick:Point .
|
||||
}
|
||||
?subClass rdfs:subClassOf* ?subRoot .
|
||||
}
|
||||
GROUP BY ?subRoot
|
||||
}
|
||||
FILTER(?subClasses > 3)
|
||||
}
|
||||
"""
|
||||
)
|
||||
for row in query:
|
||||
sub_root = row.get("subRoot").toPython().split("#")[-1]
|
||||
BrickStore.root_classes.append(sub_root)
|
||||
|
||||
@classmethod
|
||||
def load_namespaces(cls):
|
||||
BrickStore.namespaces = []
|
||||
keyword_filter = ["brickschema.org", "schema.org", "w3.org", "purl.org", "rdfs.org", "qudt.org", "ashrae.org"]
|
||||
keyword_filter = ["brickschema.org", "schema.org", "w3.org", "purl.org", "rdfs.org", "qudt.org", "ashrae.org", "usefulinc.com", "xmlns.com", "opengis.net"]
|
||||
for alias, uri in BrickStore.graph.namespaces():
|
||||
ignore_namespace = False
|
||||
for keyword in keyword_filter:
|
||||
@@ -444,8 +478,12 @@ class BrickStore:
|
||||
"""
|
||||
PREFIX brick: <https://brickschema.org/schema/Brick#>
|
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||
PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
||||
SELECT ?class WHERE {
|
||||
?class rdfs:subClassOf* brick:{root_class} .
|
||||
FILTER NOT EXISTS {
|
||||
?class owl:deprecated true .
|
||||
}
|
||||
}
|
||||
""".replace(
|
||||
"{root_class}", root_class
|
||||
|
||||
@@ -96,11 +96,11 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
props.contracted_cost_items = json.dumps(cls.contracted_cost_items)
|
||||
|
||||
@classmethod
|
||||
def contract_cost_item(cls, cost_item_id):
|
||||
def contract_cost_item(cls, cost_item):
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
if not hasattr(cls, "contracted_cost_items"):
|
||||
cls.contracted_cost_items = json.loads(props.contracted_cost_items)
|
||||
cls.contracted_cost_items.append(cost_item_id)
|
||||
cls.contracted_cost_items.append(cost_item.id())
|
||||
props.contracted_cost_items = json.dumps(cls.contracted_cost_items)
|
||||
|
||||
@classmethod
|
||||
@@ -156,14 +156,18 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
@classmethod
|
||||
def get_highlighted_cost_item(cls):
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
if not props.active_cost_schedule_id:
|
||||
return
|
||||
if props.active_cost_item_index < len(props.cost_items):
|
||||
return tool.Ifc.get().by_id(props.cost_items[props.active_cost_item_index].ifc_definition_id)
|
||||
return None
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def load_cost_item_types(cls, cost_item=None):
|
||||
if not cost_item:
|
||||
return
|
||||
cost_item = cls.get_highlighted_cost_item()
|
||||
if not cost_item:
|
||||
return
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
props.cost_item_type_products.clear()
|
||||
# TODO implement process and resource types
|
||||
@@ -220,7 +224,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
selected_quantitites = []
|
||||
unit = ""
|
||||
for quantities in ifcopenshell.util.element.get_psets(product, qtos_only=True).values():
|
||||
for qto in (tool.Ifc.get().by_id(quantities["id"]).Quantities or []):
|
||||
for qto in tool.Ifc.get().by_id(quantities["id"]).Quantities or []:
|
||||
for quantity in cost_item.CostQuantities:
|
||||
if quantity == qto:
|
||||
selected_quantitites.append(quantity)
|
||||
@@ -509,8 +513,9 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
|
||||
if filepath:
|
||||
path=filepath
|
||||
path = filepath
|
||||
else:
|
||||
path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", "cost_schedules")
|
||||
|
||||
@@ -518,6 +523,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
os.makedirs(path)
|
||||
if format == "CSV":
|
||||
from ifc5d.ifc5Dspreadsheet import Ifc5DCsvWriter
|
||||
|
||||
writer = Ifc5DCsvWriter(file=tool.Ifc.get(), output=path, cost_schedule=cost_schedule)
|
||||
writer.write()
|
||||
elif format == "ODS":
|
||||
@@ -528,7 +534,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
elif format == "XLSX":
|
||||
from ifc5d.ifc5Dspreadsheet import Ifc5DXlsxWriter
|
||||
|
||||
writer = Ifc5DXlsxWriter(file=tool.Ifc.get(), output=path, cost_schedule=cost_schedule )
|
||||
writer = Ifc5DXlsxWriter(file=tool.Ifc.get(), output=path, cost_schedule=cost_schedule)
|
||||
writer.write()
|
||||
try:
|
||||
if path:
|
||||
@@ -539,7 +545,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
elif sys.platform == "linux":
|
||||
subprocess.call(["xdg-open", path])
|
||||
except:
|
||||
return 'Could not open file location'
|
||||
return "Could not open file location"
|
||||
|
||||
@classmethod
|
||||
def get_units(cls):
|
||||
@@ -566,7 +572,6 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
name = f"{unit.Prefix} {name}"
|
||||
return f"{unit.UnitType} / {name}"
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_cost_schedule(cls, cost_item):
|
||||
for rel in cost_item.HasAssignments or []:
|
||||
@@ -649,6 +654,8 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
|
||||
@classmethod
|
||||
def toggle_cost_item_parent_change(cls, cost_item=None):
|
||||
if not cost_item:
|
||||
return
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
if props.change_cost_item_parent:
|
||||
props.active_cost_item_id = cost_item.id()
|
||||
@@ -681,12 +688,14 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
if product:
|
||||
cost_items = ifcopenshell.util.cost.get_cost_items_for_product(product)
|
||||
if pset:
|
||||
|
||||
def get_products_from_pset(pset):
|
||||
products = []
|
||||
for rel in pset.DefinesOccurrence or []:
|
||||
if rel.is_a("IfcRelDefinesByProperties"):
|
||||
products.extend(rel.RelatedObjects)
|
||||
return products
|
||||
|
||||
products = get_products_from_pset(pset)
|
||||
for product in products or []:
|
||||
cost_items.extend(ifcopenshell.util.cost.get_cost_items_for_product(product))
|
||||
@@ -705,4 +714,4 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
currency = props.custom_currency
|
||||
return {
|
||||
"Currency": currency,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,11 +122,10 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
# place the arrow
|
||||
# NOTE: may not work correctly in EDIT mode
|
||||
bbox = tool.Blender.get_object_bounding_box(stair)
|
||||
float_is_zero = lambda f: 0.0001 >= f >= -0.0001
|
||||
arrow.location = stair.matrix_world @ Vector(
|
||||
(bbox["min_x"], (bbox["max_y"] - bbox["min_y"]) / 2, bbox["max_z"])
|
||||
)
|
||||
last_step_x = max(v.co.x for v in stair.data.vertices if float_is_zero(v.co.z - bbox["max_z"]))
|
||||
last_step_x = max(v.co.x for v in stair.data.vertices if tool.Cad.is_x(v.co.z - bbox["max_z"], 0))
|
||||
arrow.data.splines[0].points[0].co = Vector((0, 0, 0, 1))
|
||||
arrow.data.splines[0].points[1].co = Vector((last_step_x, 0, 0, 1))
|
||||
|
||||
@@ -346,6 +345,27 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
literals.append(literal_data)
|
||||
return literals
|
||||
|
||||
@classmethod
|
||||
def create_annotation_context(cls, target_view, object_type=None):
|
||||
# checking PLAN target view and annotation type that doesn't require 3d
|
||||
if target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW") and object_type not in (
|
||||
"FALL",
|
||||
"SECTION_LEVEL",
|
||||
"PLAN_LEVEL",
|
||||
):
|
||||
parent = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Plan")
|
||||
else:
|
||||
parent = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model")
|
||||
|
||||
return ifcopenshell.api.run(
|
||||
"context.add_context",
|
||||
tool.Ifc.get(),
|
||||
context_type=parent.ContextType,
|
||||
context_identifier="Annotation",
|
||||
target_view=target_view,
|
||||
parent=parent,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_annotation_context(cls, target_view, object_type=None):
|
||||
# checking PLAN target view and annotation type that doesn't require 3d
|
||||
@@ -451,7 +471,10 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
elif target_view == "REFLECTED_PLAN_VIEW":
|
||||
if location_hint:
|
||||
z = tool.Ifc.get_object(tool.Ifc.get().by_id(location_hint)).matrix_world.translation.z
|
||||
return mathutils.Matrix(((-1, 0, 0, x), (0, 1, 0, y), (0, 0, -1, z + 1.6), (0, 0, 0, 1)))
|
||||
m = mathutils.Matrix()
|
||||
m[2][2] = -1
|
||||
m.translation = (x, y, z + 1.6)
|
||||
return m
|
||||
return mathutils.Matrix(((-1, 0, 0, 0), (0, 1, 0, 0), (0, 0, -1, 0), (0, 0, 0, 1)))
|
||||
elif target_view == "ELEVATION_VIEW":
|
||||
if location_hint == "NORTH":
|
||||
@@ -678,6 +701,10 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
|
||||
)
|
||||
obj.matrix_world = mat
|
||||
|
||||
if cls.get_drawing_target_view(drawing) == "REFLECTED_PLAN_VIEW":
|
||||
obj.matrix_world[1][1] *= -1
|
||||
|
||||
tool.Geometry.record_object_position(obj)
|
||||
tool.Collector.assign(obj)
|
||||
|
||||
@@ -1484,8 +1511,8 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
else:
|
||||
base_elements = set(ifc_file.by_type("IfcElement") + ifc_file.by_type("IfcSpatialElement"))
|
||||
elements = {e for e in (elements & base_elements) if e.is_a() != "IfcSpace"}
|
||||
annotations = tool.Drawing.get_group_elements(tool.Drawing.get_drawing_group(drawing))
|
||||
elements.update(annotations)
|
||||
annotations = tool.Drawing.get_group_elements(tool.Drawing.get_drawing_group(drawing))
|
||||
elements.update(annotations)
|
||||
|
||||
exclude = pset.get("Exclude", None)
|
||||
if exclude:
|
||||
|
||||
@@ -56,8 +56,13 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
|
||||
@classmethod
|
||||
def clear_scale(cls, obj):
|
||||
# Note that clearing scale has no impact on cameras.
|
||||
if (obj.scale - Vector((1.0, 1.0, 1.0))).length > 1e-4:
|
||||
if obj.data.users == 1:
|
||||
if not obj.data:
|
||||
obj.matrix_world[0][0] = 1
|
||||
obj.matrix_world[1][1] = 1
|
||||
obj.matrix_world[2][2] = 1
|
||||
elif obj.data.users == 1:
|
||||
context_override = {}
|
||||
context_override["object"] = context_override["active_object"] = obj
|
||||
context_override["selected_objects"] = context_override["selected_editable_objects"] = [obj]
|
||||
|
||||
@@ -358,7 +358,7 @@ class Model(blenderbim.core.tool.Model):
|
||||
is_closed = False
|
||||
if curve.Segments:
|
||||
for segment in curve.Segments:
|
||||
if len(segment[0]) == 3: # IfcArcIndex
|
||||
if segment.is_a("IfcArcIndex"):
|
||||
is_arc = True
|
||||
local_point = cls.convert_unit_to_si(curve.Points.CoordList[segment[0][0] - 1])
|
||||
global_point = position @ Vector(local_point).to_3d()
|
||||
@@ -368,12 +368,13 @@ class Model(blenderbim.core.tool.Model):
|
||||
cls.vertices.append(global_point)
|
||||
cls.arcs.append([len(cls.vertices) - 2, len(cls.vertices) - 1])
|
||||
else:
|
||||
local_point = cls.convert_unit_to_si(curve.Points.CoordList[segment[0][0] - 1])
|
||||
global_point = position @ Vector(local_point).to_3d()
|
||||
cls.vertices.append(global_point)
|
||||
if is_arc:
|
||||
cls.arcs[-1].append(len(cls.vertices) - 1)
|
||||
is_arc = False
|
||||
for segment_index in segment[0][0:-1]:
|
||||
local_point = cls.convert_unit_to_si(curve.Points.CoordList[segment_index - 1])
|
||||
global_point = position @ Vector(local_point).to_3d()
|
||||
cls.vertices.append(global_point)
|
||||
if is_arc:
|
||||
cls.arcs[-1].append(len(cls.vertices) - 1)
|
||||
is_arc = False
|
||||
|
||||
if curve.Segments[0][0][0] == curve.Segments[-1][0][-1]:
|
||||
is_closed = True
|
||||
@@ -768,6 +769,11 @@ class Model(blenderbim.core.tool.Model):
|
||||
return
|
||||
tool.Ifc.run("geometry.edit_object_placement", product=element, matrix=matrix, is_si=True)
|
||||
|
||||
@classmethod
|
||||
def get_element_matrix(cls, element):
|
||||
placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
return Matrix(placement)
|
||||
|
||||
@classmethod
|
||||
def reload_body_representation(cls, obj_or_objects):
|
||||
"""Update body representation including all decomposed objects"""
|
||||
|
||||
@@ -55,7 +55,8 @@ class Search(blenderbim.core.tool.Search):
|
||||
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"location{comparison}{value}")
|
||||
if not has_instance_or_entity_filter:
|
||||
filter_group_query.insert(0, "IfcElement")
|
||||
filter_group_query.insert(0, "IfcProduct")
|
||||
filter_group_query.insert(0, "IfcTypeProduct")
|
||||
query.append(", ".join(filter_group_query))
|
||||
return " + ".join(query)
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
import bpy
|
||||
import re
|
||||
import os
|
||||
import isodate
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.sequence
|
||||
import ifcopenshell.util.date
|
||||
@@ -36,7 +35,6 @@ from datetime import datetime
|
||||
import mathutils
|
||||
import pystache
|
||||
import webbrowser
|
||||
from datetime import timedelta
|
||||
|
||||
|
||||
class Sequence(blenderbim.core.tool.Sequence):
|
||||
@@ -212,7 +210,9 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
item.calendar = ""
|
||||
item.derived_calendar = calendar.Name or "Unnamed" if calendar else ""
|
||||
|
||||
if task.TaskTime and (task.TaskTime.ScheduleStart or task.TaskTime.ScheduleFinish or task.TaskTime.ScheduleDuration):
|
||||
if task.TaskTime and (
|
||||
task.TaskTime.ScheduleStart or task.TaskTime.ScheduleFinish or task.TaskTime.ScheduleDuration
|
||||
):
|
||||
task_time = task.TaskTime
|
||||
item.start = (
|
||||
canonicalise_time(ifcopenshell.util.date.ifc2datetime(task_time.ScheduleStart))
|
||||
@@ -719,26 +719,12 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
|
||||
@classmethod
|
||||
def setup_default_task_columns(cls):
|
||||
items = [
|
||||
{
|
||||
"column_type": "IfcTaskTime",
|
||||
"name": "ScheduleStart",
|
||||
},
|
||||
{
|
||||
"column_type": "IfcTaskTime",
|
||||
"name": "ScheduleFinish",
|
||||
},
|
||||
{
|
||||
"column_type": "IfcTaskTime",
|
||||
"name": "ScheduleDuration",
|
||||
},
|
||||
]
|
||||
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.columns.clear()
|
||||
for item in items:
|
||||
default_columns = ["ScheduleStart", "ScheduleFinish", "ScheduleDuration"]
|
||||
for item in default_columns:
|
||||
new = props.columns.add()
|
||||
new.name = f"{item['column_type']}.{item['name']}"
|
||||
new.name = f"IfcTaskTime.{item}"
|
||||
new.data_type = "string"
|
||||
|
||||
@classmethod
|
||||
@@ -811,10 +797,10 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
@classmethod
|
||||
def update_visualisation_date(cls, start_date, finish_date):
|
||||
def canonicalise_time(time):
|
||||
if not time:
|
||||
return "-"
|
||||
return time.strftime("%d/%m/%y")
|
||||
|
||||
if not (start_date and finish_date):
|
||||
return
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.visualisation_start = canonicalise_time(start_date)
|
||||
props.visualisation_finish = canonicalise_time(finish_date)
|
||||
@@ -1227,21 +1213,23 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
start,
|
||||
finish,
|
||||
props.speed_animation_frames,
|
||||
isodate.parse_duration(props.speed_real_duration),
|
||||
ifcopenshell.util.date.parse_duration(props.speed_real_duration),
|
||||
)
|
||||
elif props.speed_types == "DURATION_SPEED":
|
||||
animation_duration = ifcopenshell.util.date.parse_duration(props.speed_animation_duration)
|
||||
real_duration = ifcopenshell.util.date.parse_duration(props.speed_real_duration)
|
||||
return calculate_using_duration(
|
||||
start,
|
||||
finish,
|
||||
fps,
|
||||
isodate.parse_duration(props.speed_animation_duration),
|
||||
isodate.parse_duration(props.speed_real_duration),
|
||||
animation_duration,
|
||||
real_duration,
|
||||
)
|
||||
elif props.speed_types == "MULTIPLIER_SPEED":
|
||||
return calculate_using_multiplier(
|
||||
start,
|
||||
finish,
|
||||
fps,
|
||||
1,
|
||||
props.speed_multiplier,
|
||||
)
|
||||
|
||||
@@ -1312,15 +1300,24 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
if obj.animation_data:
|
||||
obj.animation_data_clear()
|
||||
|
||||
@classmethod
|
||||
def clear_object_color(cls, obj):
|
||||
obj.color = (1.0, 1.0, 1.0, 1.0)
|
||||
|
||||
@classmethod
|
||||
def display_object(cls, obj):
|
||||
if not obj.visible_get():
|
||||
obj.hide_viewport = False
|
||||
obj.hide_render = False
|
||||
|
||||
@classmethod
|
||||
def clear_objects_animation(cls, include_blender_objects=True):
|
||||
for obj in bpy.data.objects:
|
||||
if not include_blender_objects and not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
cls.clear_object_animation(obj)
|
||||
if not obj.visible_get():
|
||||
obj.hide_viewport = False
|
||||
obj.hide_render = False
|
||||
cls.clear_object_color(obj)
|
||||
cls.display_object(obj)
|
||||
|
||||
@classmethod
|
||||
def animate_objects(cls, settings, frames, clear_previous=True, animation_type=""):
|
||||
@@ -1515,13 +1512,13 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
compare_start = schedule_start
|
||||
compare_finish = schedule_finish
|
||||
task_name = task.Name or "Unnamed"
|
||||
task_name = task_name.replace('\n', "")
|
||||
task_name = task_name.replace("\n", "")
|
||||
data = {
|
||||
"pID": task.id(),
|
||||
"pName": task_name,
|
||||
"pCaption": task_name,
|
||||
"pStart": schedule_start,
|
||||
"pEnd": schedule_finish ,
|
||||
"pEnd": schedule_finish,
|
||||
"pPlanStart": compare_start,
|
||||
"pPlanEnd": compare_finish,
|
||||
"pMile": 1 if task.IsMilestone else 0,
|
||||
@@ -1607,4 +1604,28 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
|
||||
@classmethod
|
||||
def is_sort_reversed(cls):
|
||||
return bpy.context.scene.BIMWorkScheduleProperties.is_sort_reversed
|
||||
return bpy.context.scene.BIMWorkScheduleProperties.is_sort_reversed
|
||||
|
||||
@classmethod
|
||||
def get_user_predefined_type(cls):
|
||||
predefined_type = bpy.context.scene.BIMWorkScheduleProperties.work_schedule_predefined_types
|
||||
object_type = None
|
||||
if predefined_type == "USERDEFINED":
|
||||
object_type = bpy.context.scene.BIMWorkScheduleProperties.object_type
|
||||
return predefined_type, object_type
|
||||
|
||||
@classmethod
|
||||
def add_animation_camera(cls):
|
||||
bpy.ops.object.camera_add()
|
||||
camera = bpy.context.active_object
|
||||
camera.data.lens = 26
|
||||
camera.name = "4D Camera"
|
||||
camera.location = mathutils.Vector((15, 0, 15))
|
||||
camera.rotation_euler = mathutils.Euler((1.2, 0, 1.5), "XYZ")
|
||||
for obj in bpy.context.scene.objects:
|
||||
obj.select_set(False)
|
||||
for obj in bpy.context.visible_objects:
|
||||
if not (obj.hide_get() or obj.hide_render) and obj.type != "LIGHT":
|
||||
obj.select_set(True)
|
||||
bpy.context.scene.camera = camera
|
||||
bpy.ops.view3d.camera_to_view_selected()
|
||||
|
||||
@@ -92,7 +92,11 @@ class System(blenderbim.core.tool.System):
|
||||
|
||||
@classmethod
|
||||
def get_port_relating_element(cls, port):
|
||||
return port.Nests[0].RelatingObject
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
element = port.ContainedIn[0].RelatedElement
|
||||
else:
|
||||
element = port.Nests[0].RelatingObject
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def get_port_predefined_type(cls, mep_element):
|
||||
|
||||
@@ -3,6 +3,7 @@ Feature: Cost
|
||||
|
||||
Scenario: Add cost schedule
|
||||
Given an empty IFC project
|
||||
And I set "scene.BIMCostProperties.cost_schedule_predefined_types" to "COSTPLAN"
|
||||
When I press "bim.add_cost_schedule"
|
||||
Then nothing happens
|
||||
|
||||
@@ -111,6 +112,53 @@ Scenario: Add cost item
|
||||
When I press "bim.add_cost_item(cost_item={cost_item})"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Contract Cost Item
|
||||
Given an empty IFC project
|
||||
And I set "scene.BIMCostProperties.cost_schedule_predefined_types" to "COSTPLAN"
|
||||
And I press "bim.add_cost_schedule"
|
||||
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
|
||||
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
|
||||
And I press "bim.add_summary_cost_item()"
|
||||
And I press "bim.add_cost_item(cost_item={cost_item})"
|
||||
When I press "bim.contract_cost_item(cost_item={cost_item})"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Contract All Cost Items
|
||||
Given an empty IFC project
|
||||
And I set "scene.BIMCostProperties.cost_schedule_predefined_types" to "COSTPLAN"
|
||||
And I press "bim.add_cost_schedule"
|
||||
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
|
||||
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
|
||||
And I press "bim.add_summary_cost_item()"
|
||||
And I press "bim.add_cost_item(cost_item={cost_item})"
|
||||
When I press "bim.contract_cost_items"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Expand Cost Item
|
||||
Given an empty IFC project
|
||||
And I set "scene.BIMCostProperties.cost_schedule_predefined_types" to "COSTPLAN"
|
||||
And I press "bim.add_cost_schedule"
|
||||
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
|
||||
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
|
||||
And I press "bim.add_summary_cost_item()"
|
||||
And I press "bim.add_cost_item(cost_item={cost_item})"
|
||||
And I press "bim.contract_cost_item(cost_item={cost_item})"
|
||||
When I press "bim.expand_cost_item(cost_item={cost_item})"
|
||||
Then nothing happens
|
||||
|
||||
|
||||
Scenario: Expand All Cost Items
|
||||
Given an empty IFC project
|
||||
And I set "scene.BIMCostProperties.cost_schedule_predefined_types" to "COSTPLAN"
|
||||
And I press "bim.add_cost_schedule"
|
||||
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
|
||||
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
|
||||
And I press "bim.add_summary_cost_item()"
|
||||
And I press "bim.add_cost_item(cost_item={cost_item})"
|
||||
When I press "bim.expand_cost_items"
|
||||
Then nothing happens
|
||||
|
||||
|
||||
Scenario: Enable editing cost item quantities
|
||||
Given an empty IFC project
|
||||
And I press "bim.add_cost_schedule"
|
||||
|
||||
@@ -326,7 +326,7 @@ Scenario: See the current frame date as text
|
||||
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_animation_frames" to "7"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "P1W"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "1 w"
|
||||
And I press "bim.visualise_work_schedule_date_range(work_schedule={work_schedule})"
|
||||
When I am on frame "1"
|
||||
Then the object "Timeline" has a body of "2021-01-01"
|
||||
@@ -357,7 +357,7 @@ Scenario: Animate the construction of a wall
|
||||
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_animation_frames" to "7"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "P1W"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "1 w"
|
||||
And I press "bim.visualise_work_schedule_date_range(work_schedule={work_schedule})"
|
||||
When I am on frame "1"
|
||||
Then "scene.objects.get('IfcWall/Cube').hide_viewport" is "True"
|
||||
@@ -393,7 +393,7 @@ Scenario: Animate the demolition of a wall
|
||||
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_animation_frames" to "7"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "P1W"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "1 w"
|
||||
And I press "bim.visualise_work_schedule_date_range(work_schedule={work_schedule})"
|
||||
When I am on frame "1"
|
||||
Then "scene.objects.get('IfcWall/Cube').hide_viewport" is "False"
|
||||
@@ -432,7 +432,7 @@ Scenario: Animate the operation of a wall
|
||||
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_animation_frames" to "7"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "P1W"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "1 w"
|
||||
And I press "bim.visualise_work_schedule_date_range(work_schedule={work_schedule})"
|
||||
When I am on frame "1"
|
||||
Then "scene.objects.get('IfcWall/Cube').color[:]" is "[1.0, 1.0, 1.0, 1]"
|
||||
@@ -472,7 +472,7 @@ Scenario: Animate the movement of a wall
|
||||
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_animation_frames" to "7"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "P1W"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "1 w"
|
||||
And I press "bim.visualise_work_schedule_date_range(work_schedule={work_schedule})"
|
||||
When I am on frame "1"
|
||||
Then "scene.objects.get('IfcWall/FromObject').color[:]" is "[1.0, 1.0, 1.0, 1]"
|
||||
@@ -516,7 +516,7 @@ Scenario: Animate the consumption of a wall
|
||||
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_animation_frames" to "7"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "P1W"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "1 w"
|
||||
And I press "bim.visualise_work_schedule_date_range(work_schedule={work_schedule})"
|
||||
When I am on frame "1"
|
||||
Then "scene.objects.get('IfcWall/Cube').color[:]" is "[1.0, 1.0, 1.0, 1]"
|
||||
@@ -532,6 +532,39 @@ Scenario: Animate the consumption of a wall
|
||||
Then "scene.objects.get('IfcWall/Cube').hide_render" is "True"
|
||||
|
||||
|
||||
|
||||
Scenario: Clear Previous Animation
|
||||
Given an empty IFC project
|
||||
And I press "bim.add_work_schedule"
|
||||
And the variable "work_schedule" is "IfcStore.get_file().by_type('IfcWorkSchedule')[0].id()"
|
||||
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
|
||||
And I press "bim.add_summary_task(work_schedule={work_schedule})"
|
||||
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
|
||||
And I press "bim.enable_editing_task_attributes(task={task})"
|
||||
And I set "scene.BIMWorkScheduleProperties.task_attributes.get('PredefinedType').enum_value" to "CONSTRUCTION"
|
||||
And I press "bim.edit_task"
|
||||
And I press "bim.enable_editing_task_time(task={task})"
|
||||
And I set "scene.BIMWorkScheduleProperties.task_time_attributes.get('ScheduleStart').string_value" to "2021-01-02"
|
||||
And I set "scene.BIMWorkScheduleProperties.task_time_attributes.get('ScheduleFinish').string_value" to "2021-01-06"
|
||||
And I press "bim.edit_task_time"
|
||||
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 I press "bim.assign_product(task={task})"
|
||||
And I set "scene.BIMWorkScheduleProperties.visualisation_start" to "01/01/21"
|
||||
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_animation_frames" to "7"
|
||||
And I set "scene.BIMWorkScheduleProperties.speed_real_duration" to "1 w"
|
||||
And I press "bim.visualise_work_schedule_date_range(work_schedule={work_schedule})"
|
||||
And I press "bim.clear_previous_animation"
|
||||
When I am on frame "3"
|
||||
Then "scene.objects.get('IfcWall/Cube').hide_viewport" is "False"
|
||||
And "scene.objects.get('IfcWall/Cube').hide_render" is "False"
|
||||
And "scene.objects.get('IfcWall/Cube').color[:]" is "[1.0, 1.0, 1.0, 1]"
|
||||
|
||||
Scenario: Generate Gantt Chart
|
||||
Given an empty IFC project
|
||||
And I press "bim.add_work_schedule"
|
||||
@@ -808,4 +841,13 @@ Scenario: Duplicate Task and edit sequence Relationship
|
||||
And I press "bim.assign_successor(task={nested_task_two})"
|
||||
And I press "bim.duplicate_task(task={task})"
|
||||
When I press "bim.enable_editing_task_sequence(task={nested_task_one})"
|
||||
Then nothing happens
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Add Animation Camera
|
||||
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 I press "bim.add_animation_camera"
|
||||
Then "scene.objects.get('4D Camera').name" is "4D Camera"
|
||||
|
||||
+12
-38
@@ -58,51 +58,25 @@ class IfcAttributeSetter:
|
||||
return element
|
||||
if "." not in key:
|
||||
return element
|
||||
if key[0:3] == "Qto":
|
||||
qto_name, prop = key.split(".", 1)
|
||||
qto = IfcAttributeSetter.get_element_qto(element, qto_name)
|
||||
if qto:
|
||||
IfcAttributeSetter.set_qto_property(qto, prop, value)
|
||||
return element
|
||||
pset_name, prop = key.split(".", 1)
|
||||
pset = IfcAttributeSetter.get_element_pset(element, pset_name)
|
||||
pset = ifcopenshell.util.element.get_pset(element, pset_name, should_inherit=True)
|
||||
if pset:
|
||||
IfcAttributeSetter.set_pset_property(ifc_file, pset, prop, value)
|
||||
return element
|
||||
pset = ifc_file.by_id(pset["id"])
|
||||
if pset.is_a("IfcElementQuantity"):
|
||||
IfcAttributeSetter.set_qto_property(pset, prop, value)
|
||||
else:
|
||||
IfcAttributeSetter.set_pset_property(ifc_file, pset, prop, value)
|
||||
return element
|
||||
|
||||
@staticmethod
|
||||
def get_element_qto(element, name):
|
||||
for relationship in element.IsDefinedBy:
|
||||
if (
|
||||
relationship.is_a("IfcRelDefinesByProperties")
|
||||
and relationship.RelatingPropertyDefinition.is_a("IfcElementQuantity")
|
||||
and relationship.RelatingPropertyDefinition.Name == name
|
||||
):
|
||||
return relationship.RelatingPropertyDefinition
|
||||
|
||||
@staticmethod
|
||||
def set_qto_property(qto, name, value):
|
||||
for prop in qto.Quantities:
|
||||
if prop.Name != name:
|
||||
continue
|
||||
setattr(prop, prop.is_a()[len("IfcQuantity") :] + "Value", value)
|
||||
|
||||
@staticmethod
|
||||
def get_element_pset(element, name):
|
||||
if element.is_a("IfcTypeObject"):
|
||||
if element.HasPropertySets:
|
||||
for pset in element.HasPropertySets:
|
||||
if pset.is_a("IfcPropertySet") and pset.Name == name:
|
||||
return pset
|
||||
else:
|
||||
for relationship in element.IsDefinedBy:
|
||||
if (
|
||||
relationship.is_a("IfcRelDefinesByProperties")
|
||||
and relationship.RelatingPropertyDefinition.is_a("IfcPropertySet")
|
||||
and relationship.RelatingPropertyDefinition.Name == name
|
||||
):
|
||||
return relationship.RelatingPropertyDefinition
|
||||
try:
|
||||
setattr(prop, prop.is_a()[len("IfcQuantity") :] + "Value", float(value))
|
||||
except:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def set_pset_property(ifc_file, pset, name, value):
|
||||
@@ -302,7 +276,7 @@ if __name__ == "__main__":
|
||||
parser.add_argument("-i", "--ifc", type=str, required=True, help="The IFC file")
|
||||
parser.add_argument("-s", "--spreadsheet", type=str, default="data.csv", help="The spreadsheet file")
|
||||
parser.add_argument("-f", "--format", type=str, default="csv", help="The format, chosen from csv, ods, or xlsx")
|
||||
parser.add_argument("-q", "--query", type=str, default="", help='Specify a IFC query selector, such as ".IfcWall"')
|
||||
parser.add_argument("-q", "--query", type=str, default="", help='Specify a IFC query selector, such as "IfcWall"')
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--arguments",
|
||||
@@ -315,7 +289,7 @@ if __name__ == "__main__":
|
||||
|
||||
if args.export:
|
||||
ifc_file = ifcopenshell.open(args.ifc)
|
||||
results = ifcopenshell.util.selector.Selector.parse(ifc_file, args.query)
|
||||
results = ifcopenshell.util.selector.filter_elements(ifc_file, args.query)
|
||||
ifc_csv = IfcCsv()
|
||||
ifc_csv.export(ifc_file, results, args.arguments or [], output=args.spreadsheet, format=args.format)
|
||||
elif getattr(args, "import"):
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
There are different methods of installation, depending on your situation.
|
||||
There are different methods of installation, depending on your situation. If
|
||||
you aren't sure which to choose, if you're a programmer, go for the **Pre-built
|
||||
packages**. If you aren't a programmer, go for the **BlenderBIM Add-on**.
|
||||
|
||||
1. **Pre-built packages** is recommended for users wanting to use the latest IfcOpenShell builds.
|
||||
2. **PyPI** is recommended for developers using Pip.
|
||||
3. **Conda** is recommended for developers using Anaconda.
|
||||
4. **Docker** is recommended for developers using Docker.
|
||||
5. **AWS Lambda** is recommended for developers using AWS Lambda functions.
|
||||
6. **Using the BlenderBIM Add-on** is recommended for non-developers wanting a graphical interface.
|
||||
7. **From source with precompiled binaries** is recommended for developers actively working with the Python code.
|
||||
8. **Compiling from source** is recommended for developers actively working with the C++ core.
|
||||
6. **Google Colab** is recommended for developers using Google Colab.
|
||||
7. **Using the BlenderBIM Add-on** is recommended for non-developers wanting a graphical interface.
|
||||
8. **From source with precompiled binaries** is recommended for developers actively working with the Python code.
|
||||
9. **Compiling from source** is recommended for developers actively working with the C++ core.
|
||||
|
||||
Pre-built packages
|
||||
------------------
|
||||
@@ -182,6 +185,18 @@ Gateways, etc.
|
||||
the AWS documentation. Some tools that could be useful are AWS
|
||||
CloudFormaton, AWS CDK, pulumi or terraform.
|
||||
|
||||
Google Colab
|
||||
------------
|
||||
|
||||
The Google Colab environment is based on the distribution from PyPI, but lets
|
||||
you run it in an online notebook without any local setup required. This is
|
||||
great for educators and those wanting to try it out without control on their
|
||||
local system.
|
||||
|
||||
`Click here
|
||||
<https://colab.research.google.com/drive/1S9uZQvqXRpF1z6JTiKk79M1Ln63rHHIZ?usp=sharing>`__
|
||||
to launch a simple notebook.
|
||||
|
||||
Using the BlenderBIM Add-on
|
||||
---------------------------
|
||||
|
||||
|
||||
@@ -77,19 +77,30 @@ class Usecase:
|
||||
cost_item=item, products=[slab], prop_name="NetVolume")
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {"cost_item": cost_item, "products": products or [], "prop_name": prop_name}
|
||||
self.settings = {
|
||||
"cost_item": cost_item,
|
||||
"products": products or [],
|
||||
"prop_name": prop_name,
|
||||
}
|
||||
|
||||
def execute(self):
|
||||
if self.settings["prop_name"]:
|
||||
self.quantities = set(self.settings["cost_item"].CostQuantities or [])
|
||||
print(self.settings["cost_item"].CostQuantities)
|
||||
for product in self.settings["products"]:
|
||||
ifcopenshell.api.run(
|
||||
"control.assign_control",
|
||||
self.file,
|
||||
related_object=product,
|
||||
relating_control=self.settings["cost_item"],
|
||||
)
|
||||
if self.settings["prop_name"]:
|
||||
if (
|
||||
self.settings["cost_item"].CostQuantities
|
||||
and self.settings["cost_item"].CostQuantities[0].Name.lower()
|
||||
!= self.settings["prop_name"].lower()
|
||||
):
|
||||
continue
|
||||
ifcopenshell.api.run(
|
||||
"control.assign_control",
|
||||
self.file,
|
||||
related_object=product,
|
||||
relating_control=self.settings["cost_item"],
|
||||
)
|
||||
self.add_quantity_from_related_object(product)
|
||||
if self.settings["prop_name"]:
|
||||
self.settings["cost_item"].CostQuantities = list(self.quantities)
|
||||
@@ -107,7 +118,10 @@ class Usecase:
|
||||
if not qto.is_a("IfcElementQuantity"):
|
||||
return
|
||||
for prop in qto.Quantities:
|
||||
if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == self.settings["prop_name"].lower():
|
||||
if (
|
||||
prop.is_a("IfcPhysicalSimpleQuantity")
|
||||
and prop.Name.lower() == self.settings["prop_name"].lower()
|
||||
):
|
||||
self.quantities.add(prop)
|
||||
|
||||
def update_cost_item_count(self):
|
||||
|
||||
@@ -98,4 +98,7 @@ class Usecase:
|
||||
count = 0
|
||||
for rel in self.settings["cost_item"].Controls:
|
||||
count += len(rel.RelatedObjects)
|
||||
quantity[3] = count
|
||||
if count:
|
||||
quantity[3] = count
|
||||
else:
|
||||
self.file.remove(quantity)
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ class Usecase:
|
||||
outer_curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in outer_points])
|
||||
inner_curves = []
|
||||
for inner_point in inner_points:
|
||||
inner_curves.append(self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in inner_point])
|
||||
inner_curves.append(self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in inner_point]))
|
||||
else:
|
||||
outer_curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList3D(outer_points))
|
||||
inner_curves = []
|
||||
|
||||
@@ -27,6 +27,7 @@ class Usecase:
|
||||
file,
|
||||
name="Unnamed",
|
||||
predefined_type="NOTDEFINED",
|
||||
object_type=None,
|
||||
start_time=None,
|
||||
work_plan=None,
|
||||
):
|
||||
@@ -77,6 +78,7 @@ class Usecase:
|
||||
self.settings = {
|
||||
"name": name,
|
||||
"predefined_type": predefined_type,
|
||||
"object_type": object_type,
|
||||
"start_time": start_time or datetime.now(),
|
||||
"work_plan": work_plan,
|
||||
}
|
||||
@@ -98,7 +100,8 @@ class Usecase:
|
||||
work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(
|
||||
self.settings["start_time"], "IfcDateTime"
|
||||
)
|
||||
|
||||
if self.settings["object_type"]:
|
||||
work_schedule.ObjectType = self.settings["object_type"]
|
||||
if self.settings["work_plan"]:
|
||||
ifcopenshell.api.run(
|
||||
"aggregate.assign_object",
|
||||
|
||||
@@ -60,6 +60,7 @@ class draw_settings:
|
||||
merge_cells: bool = False
|
||||
include_projection: bool = True
|
||||
prefilter: bool = True
|
||||
include_curves: bool = False
|
||||
|
||||
|
||||
def main(settings, files, iterators=None, merge_projection=True, progress_function=DO_NOTHING):
|
||||
@@ -68,6 +69,7 @@ def main(settings, files, iterators=None, merge_projection=True, progress_functi
|
||||
# this is required for serialization
|
||||
APPLY_DEFAULT_MATERIALS=True,
|
||||
DISABLE_TRIANGULATION=True,
|
||||
INCLUDE_CURVES=settings.include_curves,
|
||||
# when not doing booleans, proper solids from shells isn't a requirement
|
||||
SEW_SHELLS=settings.subtract_before_hlr,
|
||||
)
|
||||
|
||||
@@ -761,7 +761,14 @@ void SvgSerializer::write(const geometry_data& data) {
|
||||
// SVG has a coordinate system with the origin in the *upper*-left corner
|
||||
// therefore we mirror the shape along the XZ-plane.
|
||||
gp_Trsf trsf_mirror;
|
||||
trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY()));
|
||||
if (!mirror_y_) {
|
||||
trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY()));
|
||||
}
|
||||
if (mirror_x_) {
|
||||
gp_Trsf mirror_x;
|
||||
mirror_x.SetMirror(gp_Ax2(gp::Origin(), gp::DX()));
|
||||
trsf_mirror.PreMultiply(mirror_x);
|
||||
}
|
||||
BRepBuilderAPI_Transform make_transform_mirror(compound_unmirrored, trsf_mirror, true);
|
||||
make_transform_mirror.Build();
|
||||
// (When determinant < 0, copy is implied and the input is not mutated.)
|
||||
@@ -1667,6 +1674,13 @@ std::array<std::array<double, 3>, 3> SvgSerializer::resize() {
|
||||
cy = ymin * sc;
|
||||
}
|
||||
|
||||
if (mirror_y_) {
|
||||
cy = - size_->second - cy;
|
||||
}
|
||||
if (mirror_x_) {
|
||||
cx = - size_->first - cx;
|
||||
}
|
||||
|
||||
m = {{ {{sc,0,-cx}},{{0,sc,-cy}},{{0,0,1}} }};
|
||||
|
||||
float_item_list::const_iterator it;
|
||||
@@ -1704,7 +1718,14 @@ void SvgSerializer::draw_hlr(const gp_Pln& pln, const drawing_key& drawing_name)
|
||||
TopoDS_Shape hlr_compound;
|
||||
if (drawing_name.first == nullptr) {
|
||||
gp_Trsf trsf_mirror;
|
||||
trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY()));
|
||||
if (!mirror_y_) {
|
||||
trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY()));
|
||||
}
|
||||
if (mirror_x_) {
|
||||
gp_Trsf mirror_x;
|
||||
mirror_x.SetMirror(gp_Ax2(gp::Origin(), gp::DX()));
|
||||
trsf_mirror.PreMultiply(mirror_x);
|
||||
}
|
||||
BRepBuilderAPI_Transform make_transform_mirror(hlr_compound_unmirrored, trsf_mirror, true);
|
||||
make_transform_mirror.Build();
|
||||
hlr_compound = make_transform_mirror.Shape();
|
||||
|
||||
@@ -309,11 +309,16 @@ namespace {
|
||||
}
|
||||
} else {
|
||||
gp_Pnt2d tmp;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
// 0,1,2,3 -> interp over bounding box edges (i%4, (i+1)%4)
|
||||
// 4,5 -> interp over bounding box diagonals (i%4, (i+2)%4)
|
||||
// @todo use boolean_utils.h points_on_planar_face_generator?
|
||||
// ... or skip faces with inner bounds all together ?
|
||||
// ... ?
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
// @todo proper edge intersection
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
const gp_Pnt2d& a = *loop[i];
|
||||
const gp_Pnt2d& b = *loop[(i + 1) % 4];
|
||||
const gp_Pnt2d& a = *loop[i % 4];
|
||||
const gp_Pnt2d& b = *loop[(i + (i >= 4 ? 2 : 1)) % 4];
|
||||
interp(a, b, j / 16.0, tmp);
|
||||
if (fclass->Perform(tmp) == TopAbs_OUT) {
|
||||
return false;
|
||||
@@ -516,6 +521,8 @@ protected:
|
||||
bool emit_building_storeys_;
|
||||
bool no_css_;
|
||||
bool unify_inputs_;
|
||||
bool mirror_y_;
|
||||
bool mirror_x_;
|
||||
|
||||
int profile_threshold_;
|
||||
|
||||
@@ -567,6 +574,8 @@ public:
|
||||
, polygonal_(false)
|
||||
, emit_building_storeys_(true)
|
||||
, no_css_(false)
|
||||
, mirror_y_(false)
|
||||
, mirror_x_(false)
|
||||
, unify_inputs_(false)
|
||||
, profile_threshold_(-1)
|
||||
, file(0)
|
||||
@@ -708,6 +717,22 @@ public:
|
||||
return profile_threshold_;
|
||||
}
|
||||
|
||||
void setMirrorY(bool b) {
|
||||
mirror_y_ = b;
|
||||
}
|
||||
|
||||
bool getMirrorY() const {
|
||||
return mirror_y_;
|
||||
}
|
||||
|
||||
void setMirrorX(bool b) {
|
||||
mirror_x_ = b;
|
||||
}
|
||||
|
||||
bool getMirrorX() const {
|
||||
return mirror_x_;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string writeMetadata(const drawing_meta& m);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user