mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-28 07:49:59 +00:00
Compare commits
133 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c26988b292 | |||
| 46e70c92c5 | |||
| 325c3665d9 | |||
| 9a80dc14e9 | |||
| 6bda066e1b | |||
| 912b01587b | |||
| 5d4d953320 | |||
| 11ed8cc787 | |||
| d1a7b99eda | |||
| 85bdeaeef4 | |||
| a5f0de4d19 | |||
| cb3392ec70 | |||
| 1dbe42061d | |||
| a024d5c07f | |||
| ea5e3c30b6 | |||
| ebfc8ef619 | |||
| c0d8467154 | |||
| d0adbab9c2 | |||
| 8e80f456cb | |||
| 8cd9fb9015 | |||
| d9fdc0908a | |||
| a218ce5160 | |||
| b3a6b8e07f | |||
| 52700452d9 | |||
| aab011f7bf | |||
| 5f129e2357 | |||
| 4e76f36af1 | |||
| 6f3ff51d91 | |||
| 0de0422c2e | |||
| 98de86e0ba | |||
| 142e41b3ad | |||
| 1e0dc7749d | |||
| 0f29de0391 | |||
| 9933cc1b20 | |||
| fa6192e9bc | |||
| bfd4559da8 | |||
| 01f52e069e | |||
| 2fbc9e4424 | |||
| ba97da20eb | |||
| 44f5be142f | |||
| 2f951d352b | |||
| 8b23c615c3 | |||
| 7c694e5b3e | |||
| 8eeb212003 | |||
| 7bf8d58cde | |||
| b6e24559e5 | |||
| 0d87d5927e | |||
| dfd5cc4728 | |||
| 20b7ee009d | |||
| 19a3f56941 | |||
| 3b298d44c1 | |||
| 038d3bc41c | |||
| 235e89ae14 | |||
| e2de9efc33 | |||
| 8126085037 | |||
| bbe14834e4 | |||
| 45f2d301b3 | |||
| 2ecd11d71f | |||
| b42f072bd5 | |||
| 3b3668eedf | |||
| 7f7f353131 | |||
| 53594de1ff | |||
| 4fbbfb5191 | |||
| 4e1daea838 | |||
| d8ff83da73 | |||
| a7712a923f | |||
| 01b5f8f237 | |||
| b9ad15c50f | |||
| db06cff997 | |||
| f0028c0094 | |||
| 1c954801b0 | |||
| 973e4f0064 | |||
| be5d291bd9 | |||
| cbec56c2f0 | |||
| 880d0ba753 | |||
| db85689364 | |||
| 48a0d8c508 | |||
| df4cd493ee | |||
| 74132dcd49 | |||
| 5cf1dd4249 | |||
| 29bd30d3bf | |||
| 3f8a0a91b3 | |||
| 416a572664 | |||
| 388c72360d | |||
| b841afc2e4 | |||
| f10d340254 | |||
| 0d0b91ee9b | |||
| 8032333a17 | |||
| 712ec6def3 | |||
| 0d43ef0736 | |||
| b318509cba | |||
| f3b541f00a | |||
| 4f64dcdbd4 | |||
| 68acf84e26 | |||
| aaceb923ad | |||
| 074de2741a | |||
| 0e4bc320cb | |||
| 9e9412bec0 | |||
| 7e92471ac9 | |||
| 078a7eb290 | |||
| df3876b438 | |||
| d414de450b | |||
| 9e89bb5044 | |||
| aa0c42e1ec | |||
| a4daa0e3d9 | |||
| 162ac39570 | |||
| 8c73fa69a1 | |||
| 159eeeb3a4 | |||
| eb0873996f | |||
| c85826ef2e | |||
| 627cf63a4c | |||
| 744c08e13e | |||
| dd7a6a80eb | |||
| ab807e3b9b | |||
| c35be50c83 | |||
| 7ff98d2918 | |||
| 35d66f5d8c | |||
| 3f018a4195 | |||
| 578e15adb2 | |||
| c4474455e9 | |||
| 8f43854cbf | |||
| 787149da13 | |||
| 3b31f6932a | |||
| 6c9e130ca8 | |||
| f38952dc51 | |||
| 24eb149221 | |||
| ce8598a0b4 | |||
| fb9cf75412 | |||
| 68f5df73b9 | |||
| e068c523ec | |||
| aa182bf047 | |||
| 6e9639446d | |||
| c48c1c3b32 |
@@ -88,7 +88,6 @@ jobs:
|
||||
sudo /usr/bin/python -m pip install networkx
|
||||
sudo /usr/bin/python -m pip install tabulate
|
||||
sudo /usr/bin/python -m pip install python-dateutil
|
||||
sudo /usr/bin/python -m pip install mathutils
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
@@ -96,4 +95,4 @@ jobs:
|
||||
sudo /usr/bin/python tests.py
|
||||
cd ../src/ifcopenshell-python
|
||||
mv ifcopenshell ifcopenshell-local # Force testing on installed module
|
||||
make test
|
||||
make test-safe
|
||||
|
||||
@@ -114,7 +114,6 @@ jobs:
|
||||
sudo /usr/bin/python -m pip install https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
|
||||
sudo /usr/bin/python -m pip install tabulate
|
||||
sudo /usr/bin/python -m pip install python-dateutil
|
||||
sudo /usr/bin/python -m pip install mathutils
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
@@ -122,4 +121,4 @@ jobs:
|
||||
sudo /usr/bin/python tests.py
|
||||
cd ../src/ifcopenshell-python
|
||||
mv ifcopenshell ifcopenshell-local # Force testing on installed module
|
||||
make test
|
||||
make test-safe
|
||||
|
||||
@@ -439,9 +439,13 @@ if(BUILD_IFCGEOM)
|
||||
if(WASM_BUILD)
|
||||
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
else()
|
||||
# OPENCASCADE_LIBRARIES repeated N times below in order to fix cyclic dependencies - use --start-group ... --end-group instead?
|
||||
# OPENCASCADE_LIBRARIES repeated N times below in order to fix cyclic dependencies
|
||||
# tfk: --start-group ... --end-group didn't work on the apple linker when last tested
|
||||
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
if(APPLE)
|
||||
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
else()
|
||||
set(OPENCASCADE_LIBRARIES -Wl,--start-group ${OPENCASCADE_LIBRARIES} -Wl,--end-group ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT APPLE AND NOT WIN32)
|
||||
|
||||
@@ -169,7 +169,7 @@ endif
|
||||
cp -r blenderbim/* dist/blenderbim/
|
||||
|
||||
# Provides IfcOpenShell Python functionality
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-f0e03c7-$(PLATFORM)64.zip
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-6c9e130-$(PLATFORM)64.zip
|
||||
cd dist/working && unzip ifcopenshell-python*
|
||||
cp -r dist/working/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||
|
||||
|
||||
@@ -125,13 +125,14 @@ classes = [
|
||||
ui.BIM_UL_generic,
|
||||
ui.BIM_UL_topics,
|
||||
ui.BIM_ADDON_preferences,
|
||||
# Project overview
|
||||
ui.BIM_PT_project_info,
|
||||
ui.BIM_PT_project_setup,
|
||||
ui.BIM_PT_geometry,
|
||||
ui.BIM_PT_tab_grouping_and_filtering,
|
||||
# Tabs panel
|
||||
ui.BIM_PT_tabs,
|
||||
# Project overview
|
||||
ui.BIM_PT_tab_project_info,
|
||||
ui.BIM_PT_tab_project_setup,
|
||||
ui.BIM_PT_tab_geometry,
|
||||
ui.BIM_PT_tab_stakeholders,
|
||||
ui.BIM_PT_tab_grouping_and_filtering,
|
||||
# Object information
|
||||
ui.BIM_PT_tab_object_metadata,
|
||||
ui.BIM_PT_tab_misc,
|
||||
@@ -140,6 +141,7 @@ classes = [
|
||||
ui.BIM_PT_tab_representations,
|
||||
ui.BIM_PT_tab_geometric_relationships,
|
||||
ui.BIM_PT_tab_parametric_geometry,
|
||||
ui.BIM_PT_tab_profiles,
|
||||
ui.BIM_PT_tab_materials,
|
||||
ui.BIM_PT_tab_styles,
|
||||
# Drawings and documents
|
||||
@@ -150,6 +152,7 @@ classes = [
|
||||
# Services and systems
|
||||
ui.BIM_PT_tab_services,
|
||||
ui.BIM_PT_tab_services_object,
|
||||
ui.BIM_PT_tab_zones,
|
||||
# Structural analysis
|
||||
ui.BIM_PT_tab_structural,
|
||||
# Construction scheduling
|
||||
@@ -285,6 +288,7 @@ def unregister():
|
||||
"SCENE_PT_rigid_body_world",
|
||||
"SCENE_PT_audio",
|
||||
"SCENE_PT_keying_sets",
|
||||
"SCENE_PT_simulation",
|
||||
"SCENE_PT_custom_props",
|
||||
]:
|
||||
try:
|
||||
|
||||
@@ -319,6 +319,17 @@ if getattr(bpy.types, "SCENE_PT_custom_props"):
|
||||
return tool.Blender.is_tab(context, "BLENDER")
|
||||
|
||||
|
||||
# available on Blender 4.0+
|
||||
if getattr(bpy.types, "SCENE_PT_simulation", None):
|
||||
|
||||
class Override_SCENE_PT_simulation(bpy.types.SCENE_PT_simulation):
|
||||
bl_idname = "SCENE_PT_simulation_override"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "BLENDER")
|
||||
|
||||
|
||||
@persistent
|
||||
def load_post(scene):
|
||||
global global_subscription_owner
|
||||
@@ -361,6 +372,7 @@ def load_post(scene):
|
||||
"SCENE_PT_rigid_body_world",
|
||||
"SCENE_PT_audio",
|
||||
"SCENE_PT_keying_sets",
|
||||
"SCENE_PT_simulation",
|
||||
"SCENE_PT_custom_props",
|
||||
]:
|
||||
if getattr(bpy.types, panel, None):
|
||||
|
||||
@@ -260,12 +260,7 @@ def convert_property_group_from_si(property_group, skip_props=()):
|
||||
for prop_name in property_group.bl_rna.properties.keys():
|
||||
if prop_name in skip_props:
|
||||
continue
|
||||
prop_bl_rna = property_group.bl_rna.properties[prop_name]
|
||||
if prop_bl_rna.array_length > 0:
|
||||
prop_value = prop_bl_rna.default_array
|
||||
else:
|
||||
prop_value = prop_bl_rna.default
|
||||
|
||||
prop_value = tool.Blender.get_blender_prop_default_value(property_group, prop_name)
|
||||
if type(prop_value) is float:
|
||||
prop_value = prop_value * conversion_k
|
||||
elif type(prop_value) is bpy.types.bpy_prop_array:
|
||||
|
||||
@@ -494,7 +494,7 @@ class IfcImporter:
|
||||
|
||||
rep = representation
|
||||
while True:
|
||||
if rep.Items and rep.Items[0].is_a("IfcMappedItem"):
|
||||
if len(rep.Items) == 1 and rep.Items[0].is_a("IfcMappedItem"):
|
||||
rep_matrix = ifcopenshell.util.placement.get_mappeditem_transformation(rep.Items[0])
|
||||
if not np.allclose(rep_matrix, np.eye(4)):
|
||||
matrix = rep_matrix @ matrix
|
||||
@@ -547,7 +547,7 @@ class IfcImporter:
|
||||
return True
|
||||
|
||||
def is_native_swept_disk_solid(self, element, representation):
|
||||
items = representation.Items or [] # Be forgiving of invalid IFCs because Revit :(
|
||||
items = [i["item"] for i in ifcopenshell.util.representation.resolve_items(representation)]
|
||||
if len(items) == 1 and items[0].is_a("IfcSweptDiskSolid"):
|
||||
if tool.Blender.Modifier.is_railing(element):
|
||||
return False
|
||||
@@ -563,6 +563,7 @@ class IfcImporter:
|
||||
return False
|
||||
|
||||
def is_native_faceted_brep(self, representation):
|
||||
# TODO handle mapped items
|
||||
for i in representation.Items:
|
||||
if i.is_a() != "IfcFacetedBrep":
|
||||
return False
|
||||
@@ -1316,12 +1317,17 @@ class IfcImporter:
|
||||
curve.resolution_u = 2
|
||||
polyline = curve.splines.new("POLY")
|
||||
|
||||
for item in native_data["representation"].Items:
|
||||
for item_data in ifcopenshell.util.representation.resolve_items(native_data["representation"]):
|
||||
item = item_data["item"]
|
||||
matrix = item_data["matrix"]
|
||||
matrix[0][3] *= self.unit_scale
|
||||
matrix[1][3] *= self.unit_scale
|
||||
matrix[2][3] *= self.unit_scale
|
||||
# TODO: support inner radius, start param, and end param
|
||||
geometry = self.create_generic_shape(item.Directrix)
|
||||
e = geometry.edges
|
||||
v = geometry.verts
|
||||
vertices = [[v[i], v[i + 1], v[i + 2], 1] for i in range(0, len(v), 3)]
|
||||
vertices = [list(matrix @ [v[i], v[i + 1], v[i + 2], 1]) for i in range(0, len(v), 3)]
|
||||
edges = [[e[i], e[i + 1]] for i in range(0, len(e), 2)]
|
||||
v2 = None
|
||||
for edge in edges:
|
||||
@@ -1401,7 +1407,8 @@ class IfcImporter:
|
||||
context_override = {}
|
||||
context_override["object"] = context_override["active_object"] = target
|
||||
context_override["selected_objects"] = context_override["selected_editable_objects"] = objs
|
||||
bpy.ops.object.join(context_override)
|
||||
with bpy.context.temp_override(**context_override):
|
||||
bpy.ops.object.join()
|
||||
target.data.name += "-merge"
|
||||
for ifc_definition_id in id_set[group_name][1:]:
|
||||
del self.added_data[ifc_definition_id]
|
||||
@@ -1458,11 +1465,10 @@ class IfcImporter:
|
||||
types_collection.hide_viewport = False
|
||||
bpy.context.view_layer.objects.active = last_obj
|
||||
|
||||
context_override = {}
|
||||
bpy.ops.object.editmode_toggle(context_override)
|
||||
bpy.ops.mesh.tris_convert_to_quads(context_override)
|
||||
bpy.ops.mesh.normals_make_consistent(context_override)
|
||||
bpy.ops.object.editmode_toggle(context_override)
|
||||
bpy.ops.object.editmode_toggle()
|
||||
bpy.ops.mesh.tris_convert_to_quads()
|
||||
bpy.ops.mesh.normals_make_consistent()
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
types_collection.hide_viewport = True
|
||||
bpy.context.view_layer.objects.active = last_obj
|
||||
@@ -1920,7 +1926,7 @@ class IfcImporter:
|
||||
|
||||
def link_element(self, element, obj):
|
||||
self.added_data[element.id()] = obj
|
||||
IfcStore.link_element(element, obj)
|
||||
tool.Ifc.link(element, obj)
|
||||
|
||||
def set_matrix_world(self, obj, matrix_world):
|
||||
obj.matrix_world = matrix_world
|
||||
|
||||
@@ -62,14 +62,14 @@ class BoundaryDecorator:
|
||||
gpu.state.point_size_set(6)
|
||||
gpu.state.blend_set("ALPHA")
|
||||
|
||||
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind() # required to be able to change uniforms of the shader
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
# general shader
|
||||
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
|
||||
selected_vertices = []
|
||||
selected_edges = []
|
||||
|
||||
@@ -31,7 +31,7 @@ class BIM_PT_SceneBoundaries(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_geometry"
|
||||
bl_parent_id = "BIM_PT_tab_geometry"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -28,7 +28,7 @@ class BIM_PT_bsdd(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMBSDDProperties
|
||||
|
||||
@@ -36,7 +36,7 @@ class BIM_PT_classifications(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -29,7 +29,7 @@ class BIM_PT_constraints(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -29,7 +29,7 @@ class BIM_PT_context(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_geometry"
|
||||
bl_parent_id = "BIM_PT_tab_geometry"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -136,13 +136,11 @@ class CostSchedulesData:
|
||||
data["UnitBasisUnitSymbol"] = "U"
|
||||
if cost_value.Category == "*":
|
||||
is_sum = True
|
||||
cost_quantity = data["TotalCostQuantity"] or 1
|
||||
if has_unit_basis:
|
||||
data["TotalCost"] = data["TotalAppliedValue"] / data["UnitBasisValueComponent"]
|
||||
data["TotalCost"] = data["TotalAppliedValue"] * cost_quantity / data["UnitBasisValueComponent"]
|
||||
else:
|
||||
if data["TotalCostQuantity"] is not None:
|
||||
data["TotalCost"] = data["TotalAppliedValue"] * data["TotalCostQuantity"]
|
||||
else:
|
||||
data["TotalCost"] = data["TotalAppliedValue"]
|
||||
data["TotalCost"] = data["TotalAppliedValue"] * cost_quantity
|
||||
if is_sum:
|
||||
data["TotalAppliedValue"] = None
|
||||
|
||||
@@ -160,8 +158,10 @@ class CostSchedulesData:
|
||||
unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get())
|
||||
if unit:
|
||||
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
|
||||
else:
|
||||
data["UnitSymbol"] = "U"
|
||||
if quantity.is_a("IfcPhysicalSimpleQuantity"):
|
||||
measure_class = quantity.wrapped_data.declaration().as_entity().attribute_by_index(3).type_of_attribute().declared_type().name()
|
||||
if "Count" in measure_class:
|
||||
data["UnitSymbol"] = "U"
|
||||
|
||||
# same_unit_nested_cost_item = set()
|
||||
# data["DerivedTotalCostQuantity"] = None
|
||||
|
||||
@@ -30,12 +30,10 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
import ifcopenshell
|
||||
#import blenderbim.tool as tool
|
||||
#import blenderbim.core.geometry
|
||||
#import ifcopenshell
|
||||
|
||||
|
||||
class BIMCoveringProperties(PropertyGroup):
|
||||
pass
|
||||
# depth: bpy.props.FloatProperty(name="Depth", default=0.1, subtype="DISTANCE", description="Flooring depth")
|
||||
depth: bpy.props.FloatProperty(name="ceiling_height", default=2.7, subtype="DISTANCE", description="Ceiling height")
|
||||
ceiling_height: bpy.props.FloatProperty(name="ceiling_height", default=2.7, subtype="DISTANCE", description="Ceiling height")
|
||||
|
||||
@@ -92,6 +92,8 @@ class CoveringToolUI:
|
||||
def draw_default_interface(cls):
|
||||
row = cls.layout.row(align=True)
|
||||
row.prop(data=cls.props, property="rl3", text="RL")
|
||||
row = cls.layout.row(align=True)
|
||||
row.prop(data=cls.covering_props, property="ceiling_height", text="Ceiling Height")
|
||||
if AuthoringData.data["ifc_classes"]:
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
@@ -101,17 +103,26 @@ class CoveringToolUI:
|
||||
relating_type_id = int(cls.props.relating_type_id)
|
||||
type_material_usage = ifcopenshell.util.element.get_material(tool.Ifc.get().by_id(relating_type_id)).is_a()
|
||||
|
||||
if (AuthoringData.data["predefined_type"] == "FLOORING" and
|
||||
type_material_usage == "IfcMaterialLayerSet" and
|
||||
# PLEASE KEEP COMMENTS AS A REMINDER
|
||||
# elif element and bpy.context.selected_objects and element.is_a("IfcSpace"):
|
||||
# op. = row.operator("bim.add_istance_flooring_from_spaces"):
|
||||
|
||||
if (type_material_usage == "IfcMaterialLayerSet" and
|
||||
not bpy.context.selected_objects):
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
# PLEASE KEEP COMMENTS AS A REMINDER
|
||||
# elif element and bpy.context.selected_objects and element.is_a("IfcSpace"):
|
||||
# op. = row.operator("bim.add_istance_flooring_from_spaces"):
|
||||
if tool.Ifc.get_entity(collection_obj):
|
||||
op = row.operator("bim.add_instance_flooring_covering_from_cursor")
|
||||
if AuthoringData.data["predefined_type"] == "FLOORING":
|
||||
op = row.operator("bim.add_instance_flooring_covering_from_cursor")
|
||||
elif AuthoringData.data["predefined_type"] == "CEILING":
|
||||
op = row.operator("bim.add_instance_ceiling_covering_from_cursor")
|
||||
else:
|
||||
op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||
op.from_invoke = True
|
||||
if cls.props.relating_type_id.isnumeric():
|
||||
op.relating_type_id = int(cls.props.relating_type_id)
|
||||
|
||||
else:
|
||||
op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||
op.from_invoke = True
|
||||
@@ -136,7 +147,7 @@ class CoveringToolUI:
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_G")
|
||||
op = row.operator("bim.regen_selected_flooring_object")
|
||||
op = row.operator("bim.regen_selected_covering_object")
|
||||
|
||||
|
||||
|
||||
@@ -230,13 +241,22 @@ class Hotkey(bpy.types.Operator, Operator):
|
||||
bpy.ops.bim.add_instance_flooring_covering_from_cursor()
|
||||
else:
|
||||
bpy.ops.bim.add_constr_type_instance()
|
||||
elif AuthoringData.data["predefined_type"] == "CEILING":
|
||||
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||
pass
|
||||
elif tool.Ifc.get_entity(collection_obj):
|
||||
bpy.ops.bim.add_instance_ceiling_covering_from_cursor()
|
||||
else:
|
||||
bpy.ops.bim.add_constr_type_instance()
|
||||
else:
|
||||
bpy.ops.bim.add_constr_type_instance()
|
||||
|
||||
def hotkey_S_G(self):
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
if (element and
|
||||
bpy.context.selected_objects and
|
||||
element.is_a("IfcCovering") and
|
||||
AuthoringData.data["active_material_usage"] == "LAYER3"):
|
||||
bpy.ops.bim.regen_selected_covering_object()
|
||||
|
||||
if AuthoringData.data["predefined_type"] == "FLOORING":
|
||||
if element and bpy.context.selected_objects and element.is_a("IfcCovering"):
|
||||
bpy.ops.bim.regen_selected_flooring_object()
|
||||
|
||||
@@ -207,6 +207,7 @@ class ExportIfcCsv(bpy.types.Operator):
|
||||
empty=props.empty_value,
|
||||
bool_true=props.true_value,
|
||||
bool_false=props.false_value,
|
||||
concat=props.concat_value,
|
||||
sort=sort,
|
||||
groups=groups,
|
||||
summaries=summaries,
|
||||
|
||||
@@ -72,6 +72,7 @@ class CsvProperties(PropertyGroup):
|
||||
empty_value: StringProperty(default="-", name="Empty String Value")
|
||||
true_value: StringProperty(default="YES", name="True Value")
|
||||
false_value: StringProperty(default="NO", name="False Value")
|
||||
concat_value: StringProperty(default=", ", name="Concat Value")
|
||||
csv_delimiter: EnumProperty(
|
||||
items=[
|
||||
(";", ";", ""),
|
||||
|
||||
@@ -79,6 +79,8 @@ class BIM_PT_ifccsv(Panel):
|
||||
row.prop(props, "true_value")
|
||||
row = layout.row()
|
||||
row.prop(props, "false_value")
|
||||
row = layout.row()
|
||||
row.prop(props, "concat_value")
|
||||
layout.use_property_split = False
|
||||
|
||||
blenderbim.bim.helper.draw_filter(self.layout, props, SearchData, "csv")
|
||||
|
||||
@@ -218,7 +218,7 @@ class CreateAllShapes(bpy.types.Operator):
|
||||
len(shape.geometry.edges),
|
||||
len(shape.geometry.faces),
|
||||
)
|
||||
print(f"Failures: {len(failures)}")
|
||||
self.report({"INFO"}, f"Failed shapes: {len(failures)}, check the system console for details.")
|
||||
for failure in failures:
|
||||
print(failure)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -29,7 +29,7 @@ class BIM_PT_documents(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -161,9 +161,9 @@ class BaseDecorator:
|
||||
os.path.join(bpy.context.scene.BIMProperties.data_dir, "fonts", "OpenGost Type B TT.ttf")
|
||||
)
|
||||
|
||||
# 3D_POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
|
||||
self.base_shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.base_shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
|
||||
def get_camera_width_mm(self):
|
||||
# Horrific prototype code to ensure bgl draws at drawing scales
|
||||
@@ -390,8 +390,6 @@ class BaseDecorator:
|
||||
# 0 is the default font, but we're fancier than that
|
||||
font_id = self.font_id
|
||||
|
||||
dpi = context.preferences.system.dpi
|
||||
|
||||
color = context.preferences.addons["blenderbim"].preferences.decorations_colour
|
||||
|
||||
ang = -Vector((1, 0)).angle_signed(text_dir)
|
||||
@@ -411,7 +409,7 @@ class BaseDecorator:
|
||||
font_size_px = int(0.004118616 * mm_to_px) * font_size_mm / 2.5
|
||||
pos = pos - line_no * font_size_px * rotation_matrix[1]
|
||||
|
||||
blf.size(font_id, font_size_px, dpi)
|
||||
blf.size(font_id, font_size_px)
|
||||
|
||||
if box_alignment or center or vcenter:
|
||||
w, h = blf.dimensions(font_id, text)
|
||||
@@ -1563,15 +1561,15 @@ class CutDecorator:
|
||||
gpu.state.point_size_set(1)
|
||||
gpu.state.blend_set("ALPHA")
|
||||
|
||||
# 3D_POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
|
||||
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind()
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
# general shader
|
||||
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
self.shader.bind()
|
||||
|
||||
black = (0, 0, 0, 1)
|
||||
@@ -1931,7 +1929,7 @@ class DecorationsHandler:
|
||||
if cls.installed:
|
||||
cls.uninstall()
|
||||
handler = cls()
|
||||
# NOTE: we USE POST_PIXEL here so that we can use both 3D_POLYLINE_UNIFORM_COLOR
|
||||
# NOTE: we USE POST_PIXEL here so that we can use both POLYLINE_UNIFORM_COLOR
|
||||
# and drawing text in the same handler. BUT this means that we supply coordinates in WINSPACE
|
||||
cls.installed = SpaceView3D.draw_handler_add(handler, (context,), "WINDOW", "POST_PIXEL")
|
||||
|
||||
|
||||
@@ -259,7 +259,9 @@ class CreateDrawing(bpy.types.Operator):
|
||||
with profile("Combine SVG layers"):
|
||||
svg_path = self.combine_svgs(context, underlay_svg, linework_svg, annotation_svg)
|
||||
|
||||
tool.Drawing.open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg_path)
|
||||
tool.Drawing.open_with_user_command(
|
||||
context.preferences.addons["blenderbim"].preferences.svg_command, svg_path
|
||||
)
|
||||
|
||||
if self.print_all:
|
||||
bpy.ops.bim.activate_drawing(drawing=original_drawing_id, camera_view_point=False)
|
||||
@@ -534,7 +536,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
root = etree.fromstring(results)
|
||||
|
||||
group = root.find("{http://www.w3.org/2000/svg}g")
|
||||
if not group:
|
||||
if group is None:
|
||||
with open(svg_path, "wb") as svg:
|
||||
svg.write(etree.tostring(root))
|
||||
|
||||
@@ -918,7 +920,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
join_criteria = join_criteria.split(",")
|
||||
else:
|
||||
# Drawing convention states that same objects classes with the same material are merged when cut.
|
||||
join_criteria = ["class", "material.Name", 'r"Pset.*Common"."Status"']
|
||||
join_criteria = ["class", "material.Name", "/Pset_.*Common/.Status", "EPset_Status.Status"]
|
||||
|
||||
group = root.find("{http://www.w3.org/2000/svg}g")
|
||||
joined_paths = {}
|
||||
|
||||
@@ -288,9 +288,9 @@ class BaseShader:
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# 3D_POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
|
||||
self.base_shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.base_shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
|
||||
def get_shader(self):
|
||||
"""Returns shader for this type"""
|
||||
|
||||
@@ -160,7 +160,8 @@ class SheetBuilder:
|
||||
x = float(image.attrib["x"])
|
||||
y = float(image.attrib["y"])
|
||||
if image.attrib["data-type"] == "view-title":
|
||||
image.attrib["x"] = str(x - readjust.x)
|
||||
image.attrib["x"] = str(x + readjust.x)
|
||||
# negate y offset because view-title comes AFTER foreground
|
||||
image.attrib["y"] = str(y - readjust.y)
|
||||
else:
|
||||
image.attrib["x"] = str(x + readjust.x)
|
||||
|
||||
@@ -160,7 +160,7 @@ class SvgWriter:
|
||||
def find_xml_symbol_by_id(self, id):
|
||||
tree = ET.parse(self.resource_paths["Symbols"])
|
||||
xml_symbol = tree.find(f'.//*[@id="{id}"]')
|
||||
return External(xml_symbol) if xml_symbol else None
|
||||
return External(xml_symbol) if xml_symbol is not None else None
|
||||
|
||||
def add_patterns(self):
|
||||
path = self.resource_paths["Patterns"]
|
||||
|
||||
@@ -61,6 +61,7 @@ classes = (
|
||||
ui.BIM_PT_workarounds,
|
||||
ui.BIM_MT_object_set_origin,
|
||||
ui.BIM_MT_separate,
|
||||
ui.BIM_MT_hotkey_separate,
|
||||
ui.BIM_UL_representation_items,
|
||||
)
|
||||
|
||||
@@ -91,7 +92,9 @@ def register():
|
||||
addon_keymaps.append((km, kmi))
|
||||
kmi = km.keymap_items.new("bim.override_object_duplicate_move_linked_macro", "D", "PRESS", alt=True)
|
||||
addon_keymaps.append((km, kmi))
|
||||
kmi = km.keymap_items.new("bim.override_object_duplicate_move_aggregate_macro", "D", "PRESS", ctrl=True, shift=True)
|
||||
kmi = km.keymap_items.new(
|
||||
"bim.override_object_duplicate_move_aggregate_macro", "D", "PRESS", ctrl=True, shift=True
|
||||
)
|
||||
addon_keymaps.append((km, kmi))
|
||||
kmi = km.keymap_items.new("bim.override_paste_buffer", "V", "PRESS", ctrl=True)
|
||||
addon_keymaps.append((km, kmi))
|
||||
@@ -106,6 +109,9 @@ def register():
|
||||
km = wm.keyconfigs.addon.keymaps.new(name="Mesh", space_type="EMPTY")
|
||||
kmi = km.keymap_items.new("bim.override_mode_set_object", "TAB", "PRESS")
|
||||
addon_keymaps.append((km, kmi))
|
||||
kmi = km.keymap_items.new("wm.call_menu", "P", "PRESS")
|
||||
kmi.properties.name = ui.BIM_MT_hotkey_separate.bl_idname
|
||||
addon_keymaps.append((km, kmi))
|
||||
|
||||
km = wm.keyconfigs.addon.keymaps.new(name="Curve", space_type="EMPTY")
|
||||
kmi = km.keymap_items.new("bim.override_mode_set_object", "TAB", "PRESS")
|
||||
|
||||
@@ -34,6 +34,7 @@ import blenderbim.core.drawing
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.handler
|
||||
from mathutils import Vector, Matrix
|
||||
from time import time
|
||||
from blenderbim.bim import import_ifc
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
@@ -66,9 +67,12 @@ class OverrideMeshSeparate(bpy.types.Operator, Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
|
||||
# You cannot separate meshes if the representation is mapped.
|
||||
relating_type = tool.Root.get_element_type(tool.Ifc.get_entity(obj))
|
||||
relating_type = tool.Root.get_element_type(element)
|
||||
if relating_type and tool.Root.does_type_have_representations(relating_type):
|
||||
# We toggle edit mode to ensure that once representations are
|
||||
# unmapped, our Blender mesh only has a single user.
|
||||
@@ -210,18 +214,22 @@ class SwitchRepresentation(bpy.types.Operator, Operator):
|
||||
should_switch_all_meshes: bpy.props.BoolProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
target = tool.Ifc.get().by_id(self.ifc_definition_id).ContextOfItems
|
||||
target_representation = tool.Ifc.get().by_id(self.ifc_definition_id)
|
||||
target = target_representation.ContextOfItems
|
||||
is_subcontext = target.is_a("IfcGeometricRepresentationSubContext")
|
||||
for obj in set(context.selected_objects + [context.active_object]):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
if is_subcontext:
|
||||
representation = ifcopenshell.util.representation.get_representation(
|
||||
element, target.ContextType, target.ContextIdentifier, target.TargetView
|
||||
)
|
||||
if obj == context.active_object:
|
||||
representation = target_representation
|
||||
else:
|
||||
representation = ifcopenshell.util.representation.get_representation(element, target.ContextType)
|
||||
if is_subcontext:
|
||||
representation = ifcopenshell.util.representation.get_representation(
|
||||
element, target.ContextType, target.ContextIdentifier, target.TargetView
|
||||
)
|
||||
else:
|
||||
representation = ifcopenshell.util.representation.get_representation(element, target.ContextType)
|
||||
if not representation:
|
||||
continue
|
||||
core.switch_representation(
|
||||
@@ -790,7 +798,8 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
new = blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=new_obj)
|
||||
|
||||
# clean up the orphaned mesh with ifc id of the original object to avoid confusion
|
||||
if new and temp_data:
|
||||
# IfcGridAxis keeps the same mesh data (it's pointing to ifc id 0, so it's not a problem)
|
||||
if new and temp_data and not new.is_a("IfcGridAxis"):
|
||||
tool.Blender.remove_data_block(temp_data)
|
||||
|
||||
if new:
|
||||
@@ -903,14 +912,14 @@ class OverrideDuplicateMoveAggregate(bpy.types.Operator):
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=tool.Ifc.get().by_id(pset["id"]),
|
||||
properties={"Data": json.dumps(data)},
|
||||
properties={"Data": tool.Ifc.get().createIfcText(json.dumps(data))},
|
||||
)
|
||||
else:
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=tool.Ifc.get().by_id(pset["id"]),
|
||||
properties={"Parent": parent.GlobalId, "Data": json.dumps(data)},
|
||||
properties={"Parent": parent.GlobalId, "Data": tool.Ifc.get().createIfcText(json.dumps(data))},
|
||||
)
|
||||
|
||||
else:
|
||||
@@ -922,7 +931,7 @@ class OverrideDuplicateMoveAggregate(bpy.types.Operator):
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=pset,
|
||||
properties={"Parent": parent.GlobalId, "Data": json.dumps(data)},
|
||||
properties={"Parent": parent.GlobalId, "Data": tool.Ifc.get().createIfcText(json.dumps(data))},
|
||||
)
|
||||
|
||||
def add_child_to_assembly_data(new_entity):
|
||||
@@ -936,7 +945,7 @@ class OverrideDuplicateMoveAggregate(bpy.types.Operator):
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=tool.Ifc.get().by_id(parent_pset["id"]),
|
||||
properties={"Data": json.dumps(data)},
|
||||
properties={"Data": tool.Ifc.get().createIfcText(json.dumps(data))},
|
||||
)
|
||||
|
||||
def create_data_structure(entity, level=-1):
|
||||
@@ -1161,6 +1170,7 @@ class RefreshAggregate(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def _execute(self, context):
|
||||
refresh_start_time = time()
|
||||
self.new_active_obj = None
|
||||
old_to_new = {}
|
||||
|
||||
@@ -1209,7 +1219,10 @@ class RefreshAggregate(bpy.types.Operator):
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=pset,
|
||||
properties={"Parent": instance_entity.GlobalId, "Data": json.dumps(data)},
|
||||
properties={
|
||||
"Parent": instance_entity.GlobalId,
|
||||
"Data": tool.Ifc.get().createIfcText(json.dumps(data)),
|
||||
},
|
||||
)
|
||||
|
||||
part_obj = tool.Ifc.get_object(part)
|
||||
@@ -1248,7 +1261,10 @@ class RefreshAggregate(bpy.types.Operator):
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=pset,
|
||||
properties={"Parent": instance_entity.GlobalId, "Data": json.dumps(data)},
|
||||
properties={
|
||||
"Parent": instance_entity.GlobalId,
|
||||
"Data": tool.Ifc.get().createIfcText(json.dumps(data)),
|
||||
},
|
||||
)
|
||||
|
||||
part_obj = tool.Ifc.get_object(part)
|
||||
@@ -1364,6 +1380,9 @@ class RefreshAggregate(bpy.types.Operator):
|
||||
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
|
||||
operator_time = time() - refresh_start_time
|
||||
if operator_time > 10:
|
||||
self.report({"INFO"}, "Refresh Aggregate was finished in {:.2f} seconds".format(operator_time))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import blenderbim.bim
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import Panel, Menu, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
@@ -53,6 +54,22 @@ class BIM_MT_separate(Menu):
|
||||
self.layout.operator("bim.override_mesh_separate", icon="PLUGIN", text="IFC By Loose Parts").type = "LOOSE"
|
||||
|
||||
|
||||
class BIM_MT_hotkey_separate(Menu):
|
||||
bl_idname = "BIM_MT_hotkey_separate"
|
||||
bl_label = "Separate"
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.label(text="IFC Separate", icon_value=blenderbim.bim.icons["IFC"].icon_id)
|
||||
self.layout.operator("bim.override_mesh_separate", text="Selection").type = "SELECTED"
|
||||
self.layout.operator("bim.override_mesh_separate", text="By Material").type = "MATERIAL"
|
||||
self.layout.operator("bim.override_mesh_separate", text="By Loose Parts").type = "LOOSE"
|
||||
self.layout.separator()
|
||||
self.layout.label(text="Blender Separate", icon="BLENDER")
|
||||
self.layout.operator("mesh.separate", text="Selection").type = "SELECTED"
|
||||
self.layout.operator("mesh.separate", text="By Material").type = "MATERIAL"
|
||||
self.layout.operator("mesh.separate", text="By Loose Parts").type = "LOOSE"
|
||||
|
||||
|
||||
class BIM_MT_object_set_origin(Menu):
|
||||
bl_idname = "BIM_MT_object_set_origin"
|
||||
bl_label = "IFC Set Origin"
|
||||
|
||||
@@ -29,7 +29,7 @@ class BIM_PT_gis(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_geometry"
|
||||
bl_parent_id = "BIM_PT_tab_geometry"
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.use_property_split = True
|
||||
|
||||
@@ -29,7 +29,6 @@ classes = (
|
||||
operator.LoadGroups,
|
||||
operator.RemoveGroup,
|
||||
operator.SelectGroupProducts,
|
||||
operator.ToggleAssigningGroup,
|
||||
operator.ToggleGroup,
|
||||
operator.UnassignGroup,
|
||||
operator.UpdateGroup,
|
||||
|
||||
@@ -173,16 +173,6 @@ class DisableEditingGroup(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ToggleAssigningGroup(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.toggle_assigning_group"
|
||||
bl_label = "Toggle Assigning Group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
context.scene.BIMGroupProperties.is_adding = not context.scene.BIMGroupProperties.is_adding
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignGroup(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_group"
|
||||
bl_label = "Assign Group"
|
||||
|
||||
@@ -49,7 +49,6 @@ class Group(PropertyGroup):
|
||||
class BIMGroupProperties(PropertyGroup):
|
||||
group_attributes: CollectionProperty(name="Group Attributes", type=Attribute)
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
is_adding: BoolProperty(name="Is Adding", default=False)
|
||||
groups: CollectionProperty(name="Groups", type=Group)
|
||||
active_group_index: IntProperty(name="Active Group Index")
|
||||
active_group_id: IntProperty(name="Active Group Id")
|
||||
|
||||
@@ -89,9 +89,9 @@ class BIM_PT_object_groups(Panel):
|
||||
ObjectGroupsData.load()
|
||||
self.props = context.scene.BIMGroupProperties
|
||||
row = self.layout.row(align=True)
|
||||
if self.props.is_adding:
|
||||
if self.props.is_editing:
|
||||
row.label(text="Adding Groups", icon="OUTLINER")
|
||||
row.operator("bim.toggle_assigning_group", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_group_editing_ui", text="", icon="CANCEL")
|
||||
self.layout.template_list(
|
||||
"BIM_UL_object_groups",
|
||||
"",
|
||||
@@ -102,7 +102,7 @@ class BIM_PT_object_groups(Panel):
|
||||
)
|
||||
else:
|
||||
row.label(text=f"{ObjectGroupsData.data['total_groups']} Groups in IFC Project", icon="OUTLINER")
|
||||
row.operator("bim.toggle_assigning_group", text="", icon="ADD")
|
||||
row.operator("bim.load_groups", text="", icon="GREASEPENCIL")
|
||||
|
||||
for group in ObjectGroupsData.data["groups"]:
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
@@ -11,7 +11,7 @@ class IFCGIT_PT_panel(bpy.types.Panel):
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_parent_id = "BIM_PT_project_info"
|
||||
bl_parent_id = "BIM_PT_tab_project_info"
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class BIM_PT_libraries(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -40,10 +40,12 @@ classes = (
|
||||
operator.EditMaterial,
|
||||
operator.EditMaterialSetItem,
|
||||
operator.EditMaterialSetItemProfile,
|
||||
operator.EditMaterialStyle,
|
||||
operator.EnableEditingAssignedMaterial,
|
||||
operator.EnableEditingMaterial,
|
||||
operator.EnableEditingMaterialSetItem,
|
||||
operator.EnableEditingMaterialSetItemProfile,
|
||||
operator.EnableEditingMaterialStyle,
|
||||
operator.ExpandMaterialCategory,
|
||||
operator.LoadMaterials,
|
||||
operator.RemoveConstituent,
|
||||
@@ -55,6 +57,7 @@ classes = (
|
||||
operator.ReorderMaterialSetItem,
|
||||
operator.SelectByMaterial,
|
||||
operator.UnassignMaterial,
|
||||
operator.UnassignMaterialStyle,
|
||||
operator.UnlinkMaterial,
|
||||
prop.Material,
|
||||
prop.BIMMaterialProperties,
|
||||
|
||||
@@ -38,6 +38,9 @@ class MaterialsData:
|
||||
"total_materials": cls.total_materials(),
|
||||
"material_types": cls.material_types(),
|
||||
"profiles": cls.profiles(),
|
||||
"styles": cls.styles(),
|
||||
"contexts": cls.contexts(),
|
||||
"active_styles": cls.active_styles(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@@ -79,6 +82,58 @@ class MaterialsData:
|
||||
if p.ProfileName
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def contexts(cls):
|
||||
results = []
|
||||
for element in tool.Ifc.get().by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||
results.append((str(element.id()), element.ContextType or "Unnamed", ""))
|
||||
for element in tool.Ifc.get().by_type("IfcGeometricRepresentationSubContext", include_subtypes=False):
|
||||
results.append(
|
||||
(
|
||||
str(element.id()),
|
||||
"{}/{}/{}".format(
|
||||
element.ContextType or "Unnamed",
|
||||
element.ContextIdentifier or "Unnamed",
|
||||
element.TargetView or "Unnamed",
|
||||
),
|
||||
"",
|
||||
)
|
||||
)
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def styles(cls):
|
||||
return [(str(s.id()), s.Name or "Unnamed", "") for s in tool.Ifc.get().by_type("IfcSurfaceStyle") if s.Name]
|
||||
|
||||
@classmethod
|
||||
def active_styles(cls):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
results = []
|
||||
if props.materials and props.active_material_index < len(props.materials):
|
||||
material = props.materials[props.active_material_index].ifc_definition_id
|
||||
if not material:
|
||||
return results
|
||||
material = tool.Ifc.get().by_id(material)
|
||||
for definition in material.HasRepresentation:
|
||||
for representation in definition.Representations:
|
||||
if not representation.is_a("IfcStyledRepresentation"):
|
||||
continue
|
||||
context = representation.ContextOfItems
|
||||
for item in representation.Items:
|
||||
if not item.is_a("IfcStyledItem"):
|
||||
continue
|
||||
for style in item.Styles:
|
||||
if style.is_a("IfcSurfaceStyle"):
|
||||
results.append({
|
||||
"context_type": context.ContextType,
|
||||
"context_identifier": getattr(context, "ContextIdentifier", ""),
|
||||
"target_view": getattr(context, "TargetView", ""),
|
||||
"name": style.Name or "Unnamed",
|
||||
"id": style.id(),
|
||||
"context_id": context.id(),
|
||||
})
|
||||
return results
|
||||
|
||||
|
||||
class ObjectMaterialData:
|
||||
data = {}
|
||||
|
||||
@@ -118,10 +118,18 @@ class AddMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Add Material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
name: bpy.props.StringProperty(default="Default")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
row = self.layout
|
||||
row.prop(self, "name", text="Name")
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.materials.get(self.obj) if self.obj else None
|
||||
core.add_material(tool.Ifc, tool.Material, tool.Style, obj=obj)
|
||||
core.add_material(tool.Ifc, tool.Material, tool.Style, obj=obj, name=self.name)
|
||||
material_prop_purge()
|
||||
|
||||
|
||||
@@ -707,3 +715,50 @@ class ContractMaterialCategory(bpy.types.Operator):
|
||||
category.is_expanded = False
|
||||
core.load_materials(tool.Material, props.material_type)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingMaterialStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_material_style"
|
||||
bl_label = "Enable Editing Material Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
material: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props.active_material_id = self.material
|
||||
props.editing_material_type = "STYLE"
|
||||
|
||||
|
||||
class EditMaterialStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_material_style"
|
||||
bl_label = "Edit Material Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
material = tool.Ifc.get().by_id(props.active_material_id)
|
||||
style = tool.Ifc.get().by_id(int(props.styles))
|
||||
context = tool.Ifc.get().by_id(int(props.contexts))
|
||||
ifcopenshell.api.run(
|
||||
"style.assign_material_style", tool.Ifc.get(), material=material, style=style, context=context
|
||||
)
|
||||
tool.Material.disable_editing_material()
|
||||
core.load_materials(tool.Material, props.material_type)
|
||||
|
||||
|
||||
class UnassignMaterialStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.unassign_material_style"
|
||||
bl_label = "Unassign Material Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
style: bpy.props.IntProperty()
|
||||
context: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
material = tool.Ifc.get().by_id(props.materials[props.active_material_index].ifc_definition_id)
|
||||
style = tool.Ifc.get().by_id(self.style)
|
||||
context = tool.Ifc.get().by_id(self.context)
|
||||
ifcopenshell.api.run(
|
||||
"style.unassign_material_style", tool.Ifc.get(), material=material, style=style, context=context
|
||||
)
|
||||
core.load_materials(tool.Material, props.material_type)
|
||||
|
||||
@@ -103,11 +103,24 @@ def get_profiles(self, context):
|
||||
return MaterialsData.data["profiles"]
|
||||
|
||||
|
||||
def get_styles(self, context):
|
||||
if not MaterialsData.is_loaded:
|
||||
MaterialsData.load()
|
||||
return MaterialsData.data["styles"]
|
||||
|
||||
|
||||
def get_contexts(self, context):
|
||||
if not MaterialsData.is_loaded:
|
||||
MaterialsData.load()
|
||||
return MaterialsData.data["contexts"]
|
||||
|
||||
|
||||
class Material(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
is_category: BoolProperty(name="Is Category", default=False)
|
||||
is_expanded: BoolProperty(name="Is Expanded", default=True)
|
||||
has_style: BoolProperty(name="Has Style", default=True)
|
||||
total_elements: IntProperty(name="Total Elements")
|
||||
|
||||
|
||||
@@ -119,7 +132,9 @@ class BIMMaterialProperties(PropertyGroup):
|
||||
profiles: EnumProperty(items=get_profiles, name="Profiles")
|
||||
active_material_id: IntProperty(name="Active Material ID")
|
||||
material_attributes: CollectionProperty(name="Material Attributes", type=Attribute)
|
||||
editing_material_type = StringProperty(name="Editing Material Type")
|
||||
editing_material_type: StringProperty(name="Editing Material Type")
|
||||
styles: EnumProperty(items=get_styles, name="Styles")
|
||||
contexts: EnumProperty(items=get_contexts, name="Contexts")
|
||||
|
||||
|
||||
class BIMObjectMaterialProperties(PropertyGroup):
|
||||
|
||||
@@ -57,20 +57,19 @@ class BIM_PT_materials(Panel):
|
||||
row.alignment = "RIGHT"
|
||||
|
||||
if self.props.material_type == "IfcMaterial":
|
||||
if not self.props.active_material_id:
|
||||
row.operator("bim.add_material", text="", icon="ADD")
|
||||
row.operator("bim.add_material", text="", icon="ADD")
|
||||
if self.props.materials and self.props.active_material_index < len(self.props.materials):
|
||||
material = self.props.materials[self.props.active_material_index]
|
||||
if material.ifc_definition_id:
|
||||
if self.props.active_material_id:
|
||||
row.operator("bim.edit_material", text="", icon="CHECKMARK").material = material.ifc_definition_id
|
||||
row.operator("bim.disable_editing_material", text="", icon="CANCEL").material = material.ifc_definition_id
|
||||
self.draw_editable_material_attributes_ui()
|
||||
else:
|
||||
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.material = material.ifc_definition_id
|
||||
row.operator("bim.enable_editing_material", text="", icon="GREASEPENCIL").material = material.ifc_definition_id
|
||||
row.operator("bim.remove_material", text="", icon="X").material = material.ifc_definition_id
|
||||
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.material = material.ifc_definition_id
|
||||
op = row.operator("bim.enable_editing_material", text="", icon="GREASEPENCIL")
|
||||
op.material = material.ifc_definition_id
|
||||
op = row.operator("bim.enable_editing_material_style", text="", icon="SHADING_RENDERED")
|
||||
op.material = material.ifc_definition_id
|
||||
row.operator("bim.remove_material", text="", icon="X").material = material.ifc_definition_id
|
||||
|
||||
self.draw_editing_ui()
|
||||
else:
|
||||
row.operator("bim.add_material_set", text="", icon="ADD").set_type = self.props.material_type
|
||||
if self.props.materials and self.props.active_material_index < len(self.props.materials):
|
||||
@@ -82,9 +81,34 @@ class BIM_PT_materials(Panel):
|
||||
|
||||
self.layout.template_list("BIM_UL_materials", "", self.props, "materials", self.props, "active_material_index")
|
||||
|
||||
for style in MaterialsData.data["active_styles"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="", icon="SHADING_RENDERED")
|
||||
row.label(text=style["context_type"])
|
||||
row.label(text=style["context_identifier"])
|
||||
row.label(text=style["target_view"])
|
||||
row.label(text=style["name"])
|
||||
op = row.operator("bim.unassign_material_style", text="", icon="X")
|
||||
op.style = style["id"]
|
||||
op.context = style["context_id"]
|
||||
|
||||
def draw_editing_ui(self):
|
||||
if not self.props.active_material_id:
|
||||
return
|
||||
ifc_definition_id = self.props.active_material_id
|
||||
if self.props.editing_material_type == "ATTRIBUTES":
|
||||
blenderbim.bim.helper.draw_attributes(self.props.material_attributes, self.layout)
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_material", text="Save Material", icon="CHECKMARK").material = ifc_definition_id
|
||||
row.operator("bim.disable_editing_material", text="", icon="CANCEL")
|
||||
elif self.props.editing_material_type == "STYLE":
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "contexts", text="")
|
||||
row.prop(self.props, "styles", text="")
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_material_style", text="Assign Style", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_material", text="", icon="CANCEL")
|
||||
|
||||
def draw_editable_material_attributes_ui(self):
|
||||
blenderbim.bim.helper.draw_attributes(self.props.material_attributes, self.layout)
|
||||
|
||||
class BIM_PT_material(Panel):
|
||||
bl_label = "Material"
|
||||
@@ -219,9 +243,7 @@ class BIM_PT_object_material(Panel):
|
||||
elif self.props.active_material_set_item_id == set_item["id"]:
|
||||
self.draw_editable_set_item_ui(set_item)
|
||||
else:
|
||||
self.draw_read_only_set_item_ui(
|
||||
set_item, index, is_first=index == 0, is_last=index == total_items - 1
|
||||
)
|
||||
self.draw_read_only_set_item_ui(set_item, index, is_first=index == 0, is_last=index == total_items - 1)
|
||||
|
||||
def draw_editable_set_item_profile_ui(self, set_item):
|
||||
box = self.layout.box()
|
||||
@@ -267,7 +289,10 @@ class BIM_PT_object_material(Panel):
|
||||
op.old_index = index
|
||||
op.new_index = index + 1
|
||||
setattr(op, "material_set", ObjectMaterialData.data["set"]["id"])
|
||||
if not self.props.active_material_set_item_id and ObjectMaterialData.data["material_class"] != "IfcMaterialList":
|
||||
if (
|
||||
not self.props.active_material_set_item_id
|
||||
and ObjectMaterialData.data["material_class"] != "IfcMaterialList"
|
||||
):
|
||||
if "Profile" in ObjectMaterialData.data["material_class"]:
|
||||
op = row.operator("bim.enable_editing_material_set_item_profile", icon="ITALIC", text="")
|
||||
op.material_set_item = set_item["id"]
|
||||
@@ -336,3 +361,6 @@ class BIM_UL_materials(UIList):
|
||||
row2 = row.row()
|
||||
row2.alignment = "RIGHT"
|
||||
row2.label(text=str(item.total_elements))
|
||||
|
||||
if item.has_style:
|
||||
row2.label(text="", icon="SHADING_RENDERED")
|
||||
|
||||
@@ -109,7 +109,8 @@ classes = (
|
||||
space.GenerateSpacesFromWalls,
|
||||
covering.AddInstanceFlooringCoveringsFromWalls,
|
||||
covering.AddInstanceFlooringCoveringFromCursor,
|
||||
covering.RegenSelectedFlooringObject,
|
||||
covering.AddInstanceCeilingCoveringFromCursor,
|
||||
covering.RegenSelectedCoveringObject,
|
||||
space.ToggleSpaceVisibility,
|
||||
mep.FitFlowSegments,
|
||||
mep.RegenerateDistributionElement,
|
||||
|
||||
@@ -59,7 +59,7 @@ class AddArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=pset,
|
||||
properties={"Parent": element.GlobalId, "Data": json.dumps(data)},
|
||||
properties={"Parent": element.GlobalId, "Data": tool.Ifc.get().createIfcText(json.dumps(data))},
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -132,7 +132,7 @@ class EditArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Model.regenerate_array(parent, data)
|
||||
|
||||
pset = tool.Ifc.get().by_id(pset["id"])
|
||||
data = json.dumps(data)
|
||||
data = tool.Ifc.get().createIfcText(json.dumps(data))
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": data})
|
||||
|
||||
tool.Blender.Modifier.Array.set_children_lock_state(element, self.item, True)
|
||||
@@ -196,7 +196,7 @@ class RemoveArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||
else:
|
||||
del data[self.item]
|
||||
data = json.dumps(data)
|
||||
data = tool.Ifc.get().createIfcText(json.dumps(data))
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": data})
|
||||
tool.Blender.Modifier.Array.constrain_children_to_parent(element)
|
||||
|
||||
@@ -248,21 +248,30 @@ class SelectAllArrayObjects(bpy.types.Operator):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
object = context.active_object
|
||||
element = tool.Ifc.get_entity(object)
|
||||
array_pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
if not array_pset:
|
||||
self.report({"ERROR"}, f"Object is not part of an array.")
|
||||
return {"CANCELLED"}
|
||||
objects = context.selected_objects
|
||||
for object in objects:
|
||||
element = tool.Ifc.get_entity(object)
|
||||
if not element:
|
||||
self.report({"ERROR"}, f"Non IFC objects, were deselected.")
|
||||
object.select_set(False)
|
||||
|
||||
try:
|
||||
parent_element = tool.Ifc.get().by_guid(array_pset["Parent"])
|
||||
except RuntimeError:
|
||||
self.report({"ERROR"}, f"Couldn't find array parent by guid '{array_pset['Parent']}'")
|
||||
return {"CANCELLED"}
|
||||
if element:
|
||||
array_pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
if not array_pset:
|
||||
self.report({"ERROR"}, f"Objects not part of an array, were deselected.")
|
||||
object.select_set(False)
|
||||
|
||||
array_objects = tool.Blender.Modifier.Array.get_all_objects(parent_element)
|
||||
tool.Blender.set_objects_selection(context, active_object=array_objects[0], selected_objects=array_objects)
|
||||
if array_pset:
|
||||
try:
|
||||
parent_element = tool.Ifc.get().by_guid(array_pset["Parent"])
|
||||
except RuntimeError:
|
||||
self.report({"ERROR"}, f"Objects that don't have an array parent, were deselected.")
|
||||
object.select_set(False)
|
||||
|
||||
array_objects = tool.Blender.Modifier.Array.get_all_objects(parent_element)
|
||||
tool.Blender.set_objects_selection(
|
||||
context, active_object=array_objects[0], selected_objects=array_objects, clear_previous_selection=False
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -51,17 +51,46 @@ class AddInstanceFlooringCoveringFromCursor(bpy.types.Operator, tool.Ifc.Operato
|
||||
|
||||
core.add_instance_flooring_covering_from_cursor(tool.Ifc, tool.Spatial, tool.Model, tool.Type, tool.Geometry)
|
||||
|
||||
class RegenSelectedFlooringObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.regen_selected_flooring_object"
|
||||
bl_label = "Regen Flooring"
|
||||
class AddInstanceCeilingCoveringFromCursor(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_instance_ceiling_covering_from_cursor"
|
||||
bl_label = "Add Ceiling From Cursor"
|
||||
bl_options = {"REGISTER"}
|
||||
bl_description = "Regen selected flooring object"
|
||||
bl_description = "Add a typed instance ceiling covering from cursor position. Move the cursor position into the desired position, select the right space collection and run the operator"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
return tool.Ifc.get_entity(collection_obj)
|
||||
|
||||
def _execute(self, context):
|
||||
|
||||
def msg(self, context):
|
||||
self.layout.label(text="NO ACTIVE STOREY")
|
||||
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
if not collection_obj:
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
return
|
||||
spatial_element = tool.Ifc.get_entity(collection_obj)
|
||||
if not spatial_element:
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
return
|
||||
|
||||
core.add_instance_ceiling_covering_from_cursor(tool.Ifc, tool.Spatial, tool.Model, tool.Type, tool.Geometry, tool.Covering)
|
||||
|
||||
class RegenSelectedCoveringObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.regen_selected_covering_object"
|
||||
bl_label = "Regen"
|
||||
bl_options = {"REGISTER"}
|
||||
bl_description = "Regen selected covering object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
return element.is_a("IfcCovering")
|
||||
return element and element.is_a("IfcCovering")
|
||||
|
||||
def _execute(self, context):
|
||||
|
||||
@@ -74,7 +103,7 @@ class RegenSelectedFlooringObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
return
|
||||
|
||||
core.regen_selected_flooring_object(tool.Ifc, tool.Spatial, tool.Model, tool.Type, tool.Geometry)
|
||||
core.regen_selected_covering_object(tool.Ifc, tool.Spatial, tool.Model, tool.Type, tool.Geometry)
|
||||
|
||||
|
||||
class AddInstanceFlooringCoveringsFromWalls(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@@ -213,6 +213,7 @@ class AuthoringData:
|
||||
if bpy.context.active_object:
|
||||
representation = tool.Geometry.get_active_representation(bpy.context.active_object)
|
||||
if representation and representation.is_a("IfcShapeRepresentation"):
|
||||
representation = tool.Geometry.resolve_mapped_representation(representation)
|
||||
return representation.RepresentationType
|
||||
|
||||
@classmethod
|
||||
@@ -313,6 +314,7 @@ class StairData:
|
||||
if not cls.data["pset_data"]:
|
||||
return
|
||||
cls.data["general_params"] = cls.general_params()
|
||||
cls.data["calculated_params"] = cls.calculated_params()
|
||||
|
||||
@classmethod
|
||||
def pset_data(cls):
|
||||
@@ -329,6 +331,10 @@ class StairData:
|
||||
general_params[prop_readable_name] = prop_value
|
||||
return general_params
|
||||
|
||||
@classmethod
|
||||
def calculated_params(cls):
|
||||
return tool.Model.get_active_stair_calculated_params(cls.data["pset_data"]["data_dict"])
|
||||
|
||||
|
||||
class SverchokData:
|
||||
data = {}
|
||||
@@ -355,7 +361,7 @@ class SverchokData:
|
||||
|
||||
|
||||
def get_prop_from_data(props, data, prop_name):
|
||||
prop_value = data[prop_name]
|
||||
prop_value = data.get(prop_name, tool.Blender.get_blender_prop_default_value(props, prop_name))
|
||||
prop_value = round(prop_value, 5) if type(prop_value) is float else prop_value
|
||||
prop_readable_name = props.bl_rna.properties[prop_name].name
|
||||
return prop_readable_name, prop_value
|
||||
|
||||
@@ -191,15 +191,15 @@ class ProfileDecorator:
|
||||
unselected_edges.append(edge_indices)
|
||||
|
||||
### Actually drawing
|
||||
# 3D_POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
|
||||
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind()
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
# general shader
|
||||
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
self.shader.bind()
|
||||
|
||||
# Draw faces
|
||||
|
||||
@@ -549,7 +549,7 @@ class AddDoor(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=pset,
|
||||
properties={"Data": json.dumps(door_data, default=list)},
|
||||
properties={"Data": tool.Ifc.get().createIfcText(json.dumps(door_data, default=list))},
|
||||
)
|
||||
update_door_modifier_representation(context, obj)
|
||||
return {"FINISHED"}
|
||||
@@ -608,7 +608,7 @@ class FinishEditingDoor(bpy.types.Operator, tool.Ifc.Operator):
|
||||
update_door_modifier_representation(context, obj)
|
||||
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Door")
|
||||
door_data = json.dumps(door_data, default=list)
|
||||
door_data = tool.Ifc.get().createIfcText(json.dumps(door_data, default=list))
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": door_data})
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -18,12 +18,10 @@
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.api
|
||||
import blenderbim.core.spatial
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import Operator
|
||||
from bpy.props import FloatProperty, IntProperty
|
||||
from mathutils import Vector
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
def add_object(self, context):
|
||||
@@ -66,7 +64,7 @@ def add_object(self, context):
|
||||
tool.Ifc.get(),
|
||||
**{"axis_tag": tag, "uvw_axes": "UAxes", "grid": grid},
|
||||
)
|
||||
IfcStore.link_element(result, obj)
|
||||
tool.Ifc.link(result, obj)
|
||||
ifcopenshell.api.run("grid.create_axis_curve", tool.Ifc.get(), **{"axis_curve": obj, "grid_axis": result})
|
||||
obj.BIMObjectProperties.ifc_definition_id = result.id()
|
||||
|
||||
@@ -91,7 +89,7 @@ def add_object(self, context):
|
||||
tool.Ifc.get(),
|
||||
**{"axis_tag": tag, "uvw_axes": "VAxes", "grid": grid},
|
||||
)
|
||||
IfcStore.link_element(result, obj)
|
||||
tool.Ifc.link(result, obj)
|
||||
ifcopenshell.api.run("grid.create_axis_curve", tool.Ifc.get(), **{"axis_curve": obj, "grid_axis": result})
|
||||
obj.BIMObjectProperties.ifc_definition_id = result.id()
|
||||
|
||||
|
||||
@@ -451,11 +451,12 @@ class MEPGenerator:
|
||||
# NOTE: I have a feeling that there are cases where order
|
||||
# in which we're checking the segments is important
|
||||
# but I couldn't pin it down to exact cases
|
||||
for test_segment_data in fitting_data[:]:
|
||||
for test_segment_data in fitting_data:
|
||||
for base_segment_data in segments_data:
|
||||
if not are_segments_compatible(test_segment_data, base_segment_data):
|
||||
continue
|
||||
segments_data.remove(test_segment_data)
|
||||
segments_data.remove(base_segment_data)
|
||||
break
|
||||
|
||||
# all segments were sorted
|
||||
return len(segments_data) == 0
|
||||
@@ -856,7 +857,7 @@ class MEPAddTransition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=pset,
|
||||
properties={"Data": json.dumps(transition_data, default=list)},
|
||||
properties={"Data": tool.Ifc.get().createIfcText(json.dumps(transition_data, default=list))},
|
||||
)
|
||||
tool.System.add_ports(obj, offset_end_port=profile_offset_si)
|
||||
|
||||
@@ -1008,6 +1009,8 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
|
||||
segments_intersection_ws, (end_segment_data["start_point"], end_segment_data["end_point"])
|
||||
)
|
||||
|
||||
# start_/end_segment_sign indicate
|
||||
# whether segments' z axes are directed towards the bend
|
||||
start_port = points_ports_map[start_point]
|
||||
end_port = points_ports_map[end_point]
|
||||
start_point_on_origin = start_point == start_segment_data["start_point"]
|
||||
@@ -1076,9 +1079,9 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
|
||||
radial_offset = V(0, 0, 0)
|
||||
ref_point_radius = self.radius + profile_dim[lateral_axis]
|
||||
radial_offset[lateral_axis] = ref_point_radius * (1 - cos(angle)) * lateral_sign
|
||||
radial_offset.z = ref_point_radius * sin(angle)
|
||||
second_object_offset = radial_offset + V(0, 0, self.start_length)
|
||||
second_object_offset += z_axis_end_object_local * (self.end_length * -end_segment_sign)
|
||||
radial_offset.z = ref_point_radius * sin(angle) * start_segment_sign
|
||||
end_port_offset = radial_offset + V(0, 0, self.start_length * start_segment_sign)
|
||||
end_port_offset += z_axis_end_object_local * (self.end_length * -end_segment_sign)
|
||||
|
||||
def get_segments_extend():
|
||||
segments_intersection = segments_intersection_ws - start_point
|
||||
@@ -1200,9 +1203,9 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=pset,
|
||||
properties={"Data": json.dumps(bend_data, default=list)},
|
||||
properties={"Data": tool.Ifc.get().createIfcText(json.dumps(bend_data, default=list))},
|
||||
)
|
||||
tool.System.add_ports(obj, end_port_pos=second_object_offset)
|
||||
tool.System.add_ports(obj, end_port_pos=end_port_offset)
|
||||
|
||||
# NOTE: at this point we loose current blender objects selection
|
||||
# create transition element
|
||||
|
||||
@@ -528,17 +528,7 @@ class AddBoolean(Operator, tool.Ifc.Operator):
|
||||
"geometry.add_boolean", tool.Ifc.get(), representation=representation, operator="DIFFERENCE", **mesh_data
|
||||
)
|
||||
|
||||
pset = ifcopenshell.util.element.get_pset(element1, "BBIM_Boolean")
|
||||
if pset:
|
||||
pset = tool.Ifc.get().by_id(pset["id"])
|
||||
data = json.loads(pset["Data"])
|
||||
data.extend([b.id() for b in booleans])
|
||||
data = list(set(data))
|
||||
else:
|
||||
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element1, name="BBIM_Boolean")
|
||||
data = [b.id() for b in booleans]
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": json.dumps(data)})
|
||||
|
||||
tool.Model.mark_manual_booleans(element1, booleans)
|
||||
tool.Model.clear_scene_openings()
|
||||
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
@@ -672,17 +662,17 @@ class RemoveBooleans(Operator, tool.Ifc.Operator, AddObjectHelper):
|
||||
except:
|
||||
continue
|
||||
|
||||
boolean_id = None
|
||||
boolean = None
|
||||
for inverse in tool.Ifc.get().get_inverse(item):
|
||||
if inverse.is_a("IfcBooleanResult"):
|
||||
boolean_id = inverse.id()
|
||||
boolean = inverse
|
||||
break
|
||||
ifcopenshell.api.run("geometry.remove_boolean", tool.Ifc.get(), item=item)
|
||||
|
||||
if obj.data.BIMMeshProperties.obj:
|
||||
upstream_obj = obj.data.BIMMeshProperties.obj
|
||||
element = tool.Ifc.get_entity(upstream_obj)
|
||||
bbim_boolean_updates.setdefault(element, []).append(boolean_id)
|
||||
bbim_boolean_updates.setdefault(element, []).append(boolean)
|
||||
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if body:
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
@@ -696,18 +686,8 @@ class RemoveBooleans(Operator, tool.Ifc.Operator, AddObjectHelper):
|
||||
)
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
for element, boolean_ids in bbim_boolean_updates.items():
|
||||
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean")
|
||||
if not pset:
|
||||
continue
|
||||
data = set(json.loads(pset["Data"]))
|
||||
data -= set(boolean_ids)
|
||||
data = list(data)
|
||||
pset = tool.Ifc.get().by_id(pset["id"])
|
||||
if data:
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": json.dumps(data)})
|
||||
else:
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||
for element, booleans in bbim_boolean_updates.items():
|
||||
tool.Model.unmark_manual_booleans(element, booleans)
|
||||
|
||||
tool.Blender.set_active_object(upstream_obj)
|
||||
return {"FINISHED"}
|
||||
@@ -801,7 +781,7 @@ class EditOpenings(Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_all_building_objects_of_similar_openings(self, opening):
|
||||
if not opening.HasFillings:
|
||||
if not opening.is_a("IfcOpeningElement") or not opening.HasFillings:
|
||||
return []
|
||||
results = set()
|
||||
for rel in opening.HasFillings:
|
||||
@@ -893,14 +873,14 @@ class DecorationsHandler:
|
||||
if not obj:
|
||||
continue
|
||||
|
||||
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind() # required to be able to change uniforms of the shader
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
# general shader
|
||||
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
|
||||
verts = []
|
||||
selected_edges = []
|
||||
|
||||
@@ -249,6 +249,8 @@ class AddConstrTypeInstance(bpy.types.Operator):
|
||||
mat.translation *= unit_scale
|
||||
mat = obj.matrix_world @ mat
|
||||
new_port = tool.Ifc.run("root.create_entity", ifc_class="IfcDistributionPort")
|
||||
new_port.PredefinedType = port.PredefinedType
|
||||
new_port.SystemType = port.SystemType
|
||||
tool.Ifc.run("system.assign_port", element=element, port=new_port)
|
||||
tool.Ifc.run("geometry.edit_object_placement", product=new_port, matrix=mat, is_si=True)
|
||||
|
||||
@@ -559,5 +561,6 @@ def ensure_material_unassigned(usecase_path, ifc_file, settings):
|
||||
total_removed = 0
|
||||
for i in to_remove:
|
||||
obj.active_material_index = i - total_removed
|
||||
bpy.ops.object.material_slot_remove({"object": obj})
|
||||
with bpy.context.temp_override(object=obj):
|
||||
bpy.ops.object.material_slot_remove()
|
||||
total_removed += 1
|
||||
|
||||
@@ -449,6 +449,16 @@ class DumbProfileJoiner:
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=new_axis
|
||||
)
|
||||
|
||||
def get_placement_axes(body_representation):
|
||||
if not body_representation:
|
||||
return None, None
|
||||
extrusion = tool.Model.get_extrusion(body_representation)
|
||||
if not extrusion:
|
||||
return None, None
|
||||
position = extrusion.Position
|
||||
return (position.Axis.DirectionRatios, position.RefDirection.DirectionRatios)
|
||||
|
||||
old_body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
new_body = ifcopenshell.api.run(
|
||||
"geometry.add_profile_representation",
|
||||
tool.Ifc.get(),
|
||||
@@ -457,9 +467,9 @@ class DumbProfileJoiner:
|
||||
depth=depth,
|
||||
cardinal_point=usage.CardinalPoint if usage else None,
|
||||
clippings=self.clippings,
|
||||
placement_zx_axes=get_placement_axes(old_body),
|
||||
)
|
||||
|
||||
old_body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if old_body:
|
||||
for inverse in tool.Ifc.get().get_inverse(old_body):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_body, new_body)
|
||||
@@ -573,15 +583,11 @@ class DumbProfileJoiner:
|
||||
if connection1 == "ATEND":
|
||||
if tool.Cad.is_x(abs(xy_angle), (0, 90, 180), tolerance=0.001) and is_orthogonal:
|
||||
plane = self.get_profile_plane(profile2, furthest_plane)
|
||||
intersect = mathutils.geometry.intersect_line_plane(
|
||||
*axis1, plane.translation, plane.col[2].to_3d()
|
||||
)
|
||||
intersect = mathutils.geometry.intersect_line_plane(*axis1, plane.translation, plane.col[2].to_3d())
|
||||
self.body[1] = intersect
|
||||
else:
|
||||
plane = self.get_profile_plane(profile2, furthest_plane, z_inwards=False)
|
||||
intersect = mathutils.geometry.intersect_line_plane(
|
||||
*axis1, plane.translation, plane.col[2].to_3d()
|
||||
)
|
||||
intersect = mathutils.geometry.intersect_line_plane(*axis1, plane.translation, plane.col[2].to_3d())
|
||||
max_dim = self.get_max_bound_box_dimension(profile1)
|
||||
self.body[1] = intersect + profile1.matrix_world.to_quaternion() @ Vector((0, 0, max_dim))
|
||||
|
||||
@@ -624,15 +630,11 @@ class DumbProfileJoiner:
|
||||
elif connection1 == "ATSTART":
|
||||
if tool.Cad.is_x(abs(xy_angle), (0, 90, 180), tolerance=0.001) and is_orthogonal:
|
||||
plane = self.get_profile_plane(profile2, furthest_plane)
|
||||
intersect = mathutils.geometry.intersect_line_plane(
|
||||
*axis1, plane.translation, plane.col[2].to_3d()
|
||||
)
|
||||
intersect = mathutils.geometry.intersect_line_plane(*axis1, plane.translation, plane.col[2].to_3d())
|
||||
self.body[0] = intersect
|
||||
else:
|
||||
plane = self.get_profile_plane(profile2, furthest_plane, z_inwards=False)
|
||||
intersect = mathutils.geometry.intersect_line_plane(
|
||||
*axis1, plane.translation, plane.col[2].to_3d()
|
||||
)
|
||||
intersect = mathutils.geometry.intersect_line_plane(*axis1, plane.translation, plane.col[2].to_3d())
|
||||
max_dim = self.get_max_bound_box_dimension(profile1)
|
||||
self.body[0] = intersect - profile1.matrix_world.to_quaternion() @ Vector((0, 0, max_dim))
|
||||
|
||||
@@ -676,9 +678,7 @@ class DumbProfileJoiner:
|
||||
if connection1 == "ATEND":
|
||||
if tool.Cad.is_x(abs(xy_angle), (0, 90, 180), tolerance=0.001) and is_orthogonal:
|
||||
plane = self.get_profile_plane(profile2, furthest_plane if is_relating else closest_plane)
|
||||
intersect = mathutils.geometry.intersect_line_plane(
|
||||
*axis1, plane.translation, plane.col[2].to_3d()
|
||||
)
|
||||
intersect = mathutils.geometry.intersect_line_plane(*axis1, plane.translation, plane.col[2].to_3d())
|
||||
self.body[1] = intersect
|
||||
else:
|
||||
plane = self.get_profile_plane(
|
||||
@@ -686,9 +686,7 @@ class DumbProfileJoiner:
|
||||
furthest_plane if is_relating else closest_plane,
|
||||
z_inwards=False if is_relating else True,
|
||||
)
|
||||
intersect = mathutils.geometry.intersect_line_plane(
|
||||
*axis1, plane.translation, plane.col[2].to_3d()
|
||||
)
|
||||
intersect = mathutils.geometry.intersect_line_plane(*axis1, plane.translation, plane.col[2].to_3d())
|
||||
max_dim = self.get_max_bound_box_dimension(profile1)
|
||||
self.body[1] = intersect + profile1.matrix_world.to_quaternion() @ Vector((0, 0, max_dim))
|
||||
self.clippings.append(
|
||||
@@ -701,9 +699,7 @@ class DumbProfileJoiner:
|
||||
elif connection1 == "ATSTART":
|
||||
if tool.Cad.is_x(abs(xy_angle), (0, 90, 180), tolerance=0.001) and is_orthogonal:
|
||||
plane = self.get_profile_plane(profile2, furthest_plane if is_relating else closest_plane)
|
||||
intersect = mathutils.geometry.intersect_line_plane(
|
||||
*axis1, plane.translation, plane.col[2].to_3d()
|
||||
)
|
||||
intersect = mathutils.geometry.intersect_line_plane(*axis1, plane.translation, plane.col[2].to_3d())
|
||||
self.body[0] = intersect
|
||||
else:
|
||||
plane = self.get_profile_plane(
|
||||
@@ -711,9 +707,7 @@ class DumbProfileJoiner:
|
||||
furthest_plane if is_relating else closest_plane,
|
||||
z_inwards=False if is_relating else True,
|
||||
)
|
||||
intersect = mathutils.geometry.intersect_line_plane(
|
||||
*axis1, plane.translation, plane.col[2].to_3d()
|
||||
)
|
||||
intersect = mathutils.geometry.intersect_line_plane(*axis1, plane.translation, plane.col[2].to_3d())
|
||||
max_dim = self.get_max_bound_box_dimension(profile1)
|
||||
self.body[0] = intersect - profile1.matrix_world.to_quaternion() @ Vector((0, 0, max_dim))
|
||||
self.clippings.append(
|
||||
|
||||
@@ -140,6 +140,7 @@ class BIMModelProperties(PropertyGroup):
|
||||
rl3: bpy.props.FloatProperty(name="RL", default=1, subtype="DISTANCE", description="Z offset for space calculation")
|
||||
x_angle: bpy.props.FloatProperty(name="X Angle", default=0, subtype="ANGLE", min=-pi / 180 * 89, max=pi / 180 * 89)
|
||||
type_page: bpy.props.IntProperty(name="Type Page", default=1, update=update_type_page)
|
||||
# fmt: off
|
||||
type_template: bpy.props.EnumProperty(
|
||||
items=(
|
||||
("MESH", "Custom Mesh", "Use as a representation currently active object mesh or default cube if no object selected"),
|
||||
@@ -159,6 +160,7 @@ class BIMModelProperties(PropertyGroup):
|
||||
name="Type Template",
|
||||
default="MESH",
|
||||
)
|
||||
# fmt: on
|
||||
type_class: bpy.props.EnumProperty(items=get_type_class, name="IFC Class", update=update_type_class)
|
||||
type_predefined_type: bpy.props.EnumProperty(items=get_type_predefined_type, name="Predefined Type", default=None)
|
||||
type_name: bpy.props.StringProperty(name="Name", default="TYPEX")
|
||||
@@ -191,6 +193,10 @@ class BIMArrayProperties(PropertyGroup):
|
||||
|
||||
|
||||
class BIMStairProperties(PropertyGroup):
|
||||
def validate_nosing_value(self, context):
|
||||
if self.stair_type != "WOOD/STEEL" and self.nosing_length < 0:
|
||||
self["nosing_length"] = 0
|
||||
|
||||
non_si_units_props = ("is_editing", "number_of_treads", "has_top_nib", "stair_type")
|
||||
stair_types = (
|
||||
("CONCRETE", "Concrete", ""),
|
||||
@@ -201,13 +207,37 @@ class BIMStairProperties(PropertyGroup):
|
||||
is_editing: bpy.props.BoolProperty(default=False)
|
||||
width: bpy.props.FloatProperty(name="Width", default=1.2, soft_min=0.01, subtype="DISTANCE")
|
||||
height: bpy.props.FloatProperty(name="Height", default=1.0, soft_min=0.01, subtype="DISTANCE")
|
||||
number_of_treads: bpy.props.IntProperty(name="Number of treads", default=6, soft_min=1)
|
||||
number_of_treads: bpy.props.IntProperty(name="Number of Treads", default=6, soft_min=1)
|
||||
tread_depth: bpy.props.FloatProperty(name="Tread Depth", default=0.25, soft_min=0.01, subtype="DISTANCE")
|
||||
tread_run: bpy.props.FloatProperty(name="Tread Run", default=0.3, soft_min=0.01, subtype="DISTANCE")
|
||||
base_slab_depth: bpy.props.FloatProperty(name="Base slab depth", default=0.25, soft_min=0, subtype="DISTANCE")
|
||||
top_slab_depth: bpy.props.FloatProperty(name="Top slab depth", default=0.25, soft_min=0, subtype="DISTANCE")
|
||||
has_top_nib: bpy.props.BoolProperty(name="Has top nib", default=True)
|
||||
stair_type: bpy.props.EnumProperty(name="Stair type", items=stair_types, default="CONCRETE")
|
||||
base_slab_depth: bpy.props.FloatProperty(name="Base Slab Depth", default=0.25, soft_min=0, subtype="DISTANCE")
|
||||
top_slab_depth: bpy.props.FloatProperty(name="Top Slab Depth", default=0.25, soft_min=0, subtype="DISTANCE")
|
||||
has_top_nib: bpy.props.BoolProperty(name="Has Top Nib", default=True)
|
||||
stair_type: bpy.props.EnumProperty(
|
||||
name="Stair Type", items=stair_types, default="CONCRETE", update=validate_nosing_value
|
||||
)
|
||||
custom_first_last_tread_run: bpy.props.FloatVectorProperty(
|
||||
name="Custom First / Last Treads Widths",
|
||||
description='Specify custom first / last treads widths, different from the general "Tread Run". Leave 0 to disable.',
|
||||
default=(0, 0),
|
||||
min=0,
|
||||
unit="LENGTH",
|
||||
size=2,
|
||||
)
|
||||
# TODO: need to clamp at zero for non WOOD/STEEL
|
||||
nosing_length: bpy.props.FloatProperty(
|
||||
name="Nosing Length",
|
||||
description=(
|
||||
"Overhang of the tread, not counted as a part of the tread run.\n"
|
||||
"Can be negative for WOOD/STEEL stair (then it becomes a tread gap)"
|
||||
),
|
||||
default=0,
|
||||
unit="LENGTH",
|
||||
update=validate_nosing_value,
|
||||
)
|
||||
nosing_depth: bpy.props.FloatProperty(
|
||||
name="Nosing Depth", description="Depth of the tread's nosing", min=0, default=0, unit="LENGTH"
|
||||
)
|
||||
|
||||
def get_props_kwargs(self, convert_to_project_units=False, stair_type=None):
|
||||
if not stair_type:
|
||||
@@ -218,10 +248,12 @@ class BIMStairProperties(PropertyGroup):
|
||||
"height": self.height,
|
||||
"number_of_treads": self.number_of_treads,
|
||||
"tread_run": self.tread_run,
|
||||
"nosing_length": self.nosing_length,
|
||||
}
|
||||
|
||||
if stair_type == "CONCRETE":
|
||||
concrete_props = {
|
||||
"nosing_depth": self.nosing_depth,
|
||||
"base_slab_depth": self.base_slab_depth,
|
||||
"top_slab_depth": self.top_slab_depth,
|
||||
"has_top_nib": self.has_top_nib,
|
||||
@@ -236,7 +268,13 @@ class BIMStairProperties(PropertyGroup):
|
||||
stair_kwargs.update(wood_steel_props)
|
||||
|
||||
elif stair_type == "GENERIC":
|
||||
pass
|
||||
generic_props = {
|
||||
"nosing_depth": self.nosing_depth,
|
||||
}
|
||||
stair_kwargs.update(generic_props)
|
||||
|
||||
# defined here to appear last in UI
|
||||
stair_kwargs["custom_first_last_tread_run"] = self.custom_first_last_tread_run
|
||||
|
||||
if not convert_to_project_units:
|
||||
return stair_kwargs
|
||||
|
||||
@@ -114,7 +114,7 @@ def update_bbim_railing_pset(element, railing_data):
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Railing")
|
||||
if not pset:
|
||||
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="BBIM_Railing")
|
||||
railing_data = json.dumps(railing_data, default=list)
|
||||
railing_data = tool.Ifc.get().createIfcText(json.dumps(railing_data, default=list))
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": railing_data})
|
||||
|
||||
|
||||
|
||||
@@ -412,7 +412,7 @@ def update_bbim_roof_pset(element, roof_data):
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Roof")
|
||||
if not pset:
|
||||
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="BBIM_Roof")
|
||||
roof_data = json.dumps(roof_data, default=list)
|
||||
roof_data = tool.Ifc.get().createIfcText(json.dumps(roof_data, default=list))
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": roof_data})
|
||||
|
||||
|
||||
|
||||
@@ -17,22 +17,17 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import json
|
||||
import bmesh
|
||||
import ifcopenshell
|
||||
import blenderbim
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector
|
||||
from bmesh.types import BMVert
|
||||
from bpy.types import Operator
|
||||
from bpy.props import FloatProperty, IntProperty
|
||||
from bpy_extras.object_utils import AddObjectHelper, object_data_add
|
||||
|
||||
import bmesh
|
||||
from bmesh.types import BMVert
|
||||
|
||||
import ifcopenshell
|
||||
from ifcopenshell.util.shape_builder import V, ShapeBuilder
|
||||
import blenderbim
|
||||
import blenderbim.tool as tool
|
||||
|
||||
from mathutils import Vector
|
||||
from pprint import pprint
|
||||
import json
|
||||
|
||||
|
||||
def add_dumb_stair_object(self, context):
|
||||
verts = [
|
||||
@@ -82,138 +77,10 @@ class BIM_OT_add_object(Operator, AddObjectHelper):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def generate_stair_2d_profile(
|
||||
number_of_treads,
|
||||
height,
|
||||
width,
|
||||
tread_run,
|
||||
stair_type,
|
||||
# WOOD/STEEL CONCRETE STAIR ARGUMENTS
|
||||
tread_depth=None,
|
||||
# CONCRETE STAIR ARGUMENTS
|
||||
has_top_nib=None,
|
||||
top_slab_depth=None,
|
||||
base_slab_depth=None,
|
||||
):
|
||||
vertices = []
|
||||
edges = []
|
||||
faces = []
|
||||
|
||||
number_of_risers = number_of_treads + 1
|
||||
tread_rise = height / number_of_risers
|
||||
length = tread_run * number_of_risers
|
||||
|
||||
if stair_type == "WOOD/STEEL":
|
||||
builder = ShapeBuilder(None)
|
||||
tread_shape = builder.get_rectangle_coords(
|
||||
size=V(tread_run, 0, tread_depth),
|
||||
position=V(0, 0, -(tread_depth-tread_rise))
|
||||
)
|
||||
tread_offset = V(tread_run, 0, tread_rise)
|
||||
|
||||
for i in range(number_of_risers):
|
||||
cur_trade_shape = [v + tread_offset * i for v in tread_shape]
|
||||
vertices.extend(cur_trade_shape)
|
||||
|
||||
cur_vertex = i * 4
|
||||
edges.extend([
|
||||
(cur_vertex, cur_vertex+1),
|
||||
(cur_vertex+1, cur_vertex+2),
|
||||
(cur_vertex+2, cur_vertex+3),
|
||||
(cur_vertex+3, cur_vertex),
|
||||
])
|
||||
faces.append(list(range(cur_vertex, cur_vertex + 1)))
|
||||
|
||||
return (vertices, edges, faces)
|
||||
|
||||
elif stair_type == "GENERIC":
|
||||
vertices.append(Vector([0, 0, 0]))
|
||||
|
||||
tread_verts = [
|
||||
Vector([0, 0, tread_rise]),
|
||||
Vector([tread_run, 0, tread_rise])
|
||||
]
|
||||
tread_offset = Vector([tread_run, 0, tread_rise])
|
||||
|
||||
for i in range(number_of_risers):
|
||||
current_tread_verts = [v + tread_offset * i for v in tread_verts]
|
||||
last_vert_i = len(vertices) - 1
|
||||
edges.extend([
|
||||
(last_vert_i, last_vert_i + 1),
|
||||
(last_vert_i + 1, last_vert_i + 2)
|
||||
])
|
||||
vertices.extend(current_tread_verts)
|
||||
|
||||
last_vert_i = len(vertices)
|
||||
vertices.append(vertices[-1] * V(1,0,0))
|
||||
edges.extend([
|
||||
(last_vert_i - 1, last_vert_i),
|
||||
(last_vert_i, 0)
|
||||
])
|
||||
|
||||
return (vertices, edges, faces)
|
||||
|
||||
elif stair_type == "CONCRETE":
|
||||
for i in range(number_of_risers):
|
||||
vertices.extend([
|
||||
Vector((tread_run*i, 0, tread_rise*i)),
|
||||
Vector((tread_run*i, 0, tread_rise*(i+1)))
|
||||
])
|
||||
cur_vertex = i * 2
|
||||
if i != 0:
|
||||
edges.append((cur_vertex - 1, cur_vertex))
|
||||
edges.append((cur_vertex, cur_vertex + 1))
|
||||
|
||||
vertices.append(Vector((tread_run * number_of_risers, 0, tread_rise * number_of_risers)))
|
||||
edges.append((number_of_risers * 2, number_of_risers * 2 - 1))
|
||||
|
||||
td_vector = Vector((vertices[2][2], 0, -vertices[2][0])).normalized() * tread_depth
|
||||
|
||||
k = tread_rise / tread_run
|
||||
s0 = vertices[0] + td_vector
|
||||
b = s0.z - k * s0.x # comes from y = kx + b
|
||||
# you could use td_vector as depth_vector
|
||||
# but then stair won't be perpendicular to the X+
|
||||
# b is kind of vertical tread_depth (along Z+)
|
||||
depth_vector = Vector((0, 0, b))
|
||||
|
||||
# top nib
|
||||
if has_top_nib:
|
||||
vertices.append( vertices[number_of_risers * 2] + Vector((0, 0, -top_slab_depth)) )
|
||||
vertices.append( vertices[number_of_risers * 2] + Vector(((-top_slab_depth - b) / k, 0, -top_slab_depth)) )
|
||||
last_vertex_i = len(vertices) - 1
|
||||
edges.append( (number_of_risers * 2, last_vertex_i - 1) )
|
||||
edges.append( (last_vertex_i - 1, last_vertex_i) )
|
||||
else:
|
||||
vertices.append(vertices[number_of_risers * 2] + depth_vector)
|
||||
last_vertex_i = len(vertices) - 1
|
||||
edges.append((number_of_risers * 2, last_vertex_i))
|
||||
|
||||
top_nib_end = len(vertices) - 1
|
||||
|
||||
# bottom nib
|
||||
if abs(b) <= base_slab_depth:
|
||||
vertices.append(vertices[0] + depth_vector)
|
||||
edges.append((0, len(vertices) - 1))
|
||||
bottom_nib_end = len(vertices) - 1
|
||||
else:
|
||||
vertices.append(vertices[0] + Vector(((-base_slab_depth - b) / k, 0, -base_slab_depth)))
|
||||
vertices.append(vertices[0] + Vector((0, 0, -base_slab_depth)))
|
||||
last_vertex_i = len(vertices) - 1
|
||||
edges.append( (0, last_vertex_i) )
|
||||
edges.append( (last_vertex_i - 1, last_vertex_i) )
|
||||
bottom_nib_end = len(vertices) - 2
|
||||
|
||||
edges.append( (bottom_nib_end, top_nib_end) )
|
||||
faces = [list(range(len(vertices)))]
|
||||
|
||||
return (vertices, edges, faces)
|
||||
|
||||
|
||||
def update_stair_modifier(context):
|
||||
def regenerate_stair_mesh(context):
|
||||
obj = context.active_object
|
||||
props_kwargs = obj.BIMStairProperties.get_props_kwargs()
|
||||
vertices, edges, faces = generate_stair_2d_profile(**props_kwargs)
|
||||
vertices, edges, faces = tool.Model.generate_stair_2d_profile(**props_kwargs)
|
||||
|
||||
obj = context.active_object
|
||||
bm = bmesh.new()
|
||||
@@ -221,7 +88,7 @@ def update_stair_modifier(context):
|
||||
bm.edges.index_update()
|
||||
|
||||
new_verts = [bm.verts.new(v) for v in vertices]
|
||||
new_edges = [bm.edges.new( (new_verts[e[0]], new_verts[e[1]]) ) for e in edges]
|
||||
new_edges = [bm.edges.new((new_verts[e[0]], new_verts[e[1]])) for e in edges]
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
|
||||
@@ -242,6 +109,26 @@ def update_stair_modifier(context):
|
||||
obj.data.update()
|
||||
|
||||
|
||||
def update_stair_representation(obj):
|
||||
body = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
representation = ifcopenshell.api.run(
|
||||
"geometry.add_representation",
|
||||
tool.Ifc.get(),
|
||||
context=body,
|
||||
blender_object=obj,
|
||||
geometry=obj.data,
|
||||
coordinate_offset=tool.Geometry.get_cartesian_point_coordinate_offset(obj),
|
||||
total_items=tool.Geometry.get_total_representation_items(obj),
|
||||
should_force_faceted_brep=tool.Geometry.should_force_faceted_brep(),
|
||||
should_force_triangulation=tool.Geometry.should_force_triangulation(),
|
||||
should_generate_uvs=tool.Geometry.should_generate_uvs(obj),
|
||||
ifc_representation_class=None,
|
||||
profile_set_usage=None,
|
||||
)
|
||||
tool.Model.replace_object_ifc_representation(body, obj, representation)
|
||||
tool.Ifc.finish_edit(obj)
|
||||
|
||||
|
||||
def update_ifc_stair_props(obj):
|
||||
"""should be called after new geometry settled
|
||||
since it's going to update ifc representation
|
||||
@@ -259,6 +146,7 @@ def update_ifc_stair_props(obj):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
riser_height = props.height / number_of_risers / si_conversion
|
||||
tread_length = props.tread_depth / si_conversion
|
||||
nosing_length = props.nosing_length / si_conversion
|
||||
|
||||
if element.is_a("IfcStairFlight"):
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
@@ -284,6 +172,7 @@ def update_ifc_stair_props(obj):
|
||||
"NumberOfTreads": props.number_of_treads,
|
||||
"RiserHeight": riser_height,
|
||||
"TreadLength": tread_length,
|
||||
"NosingLength": nosing_length,
|
||||
},
|
||||
)
|
||||
tool.Ifc.edit(obj)
|
||||
@@ -327,7 +216,7 @@ class BIM_OT_add_clever_stair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj.location = spawn_location
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection.objects.link(obj)
|
||||
|
||||
|
||||
body_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
element = blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
@@ -335,7 +224,7 @@ class BIM_OT_add_clever_stair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
ifc_class="IfcStairFlight",
|
||||
should_add_representation=True,
|
||||
should_add_representation=False,
|
||||
context=body_context,
|
||||
)
|
||||
if tool.Ifc.get_schema() != "IFC2X3":
|
||||
@@ -370,10 +259,11 @@ class AddStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"pset.edit_pset",
|
||||
ifc_file,
|
||||
pset=pset,
|
||||
properties={"Data": json.dumps(stair_data)},
|
||||
properties={"Data": tool.Ifc.get().createIfcText(json.dumps(stair_data))},
|
||||
)
|
||||
update_stair_modifier(context)
|
||||
regenerate_stair_mesh(context)
|
||||
update_ifc_stair_props(obj)
|
||||
update_stair_representation(obj)
|
||||
|
||||
|
||||
class CancelEditingStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -388,7 +278,7 @@ class CancelEditingStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props = obj.BIMStairProperties
|
||||
# restore previous settings since editing was canceled
|
||||
props.set_props_kwargs_from_ifc_data(data)
|
||||
update_stair_modifier(context)
|
||||
regenerate_stair_mesh(context)
|
||||
|
||||
props.is_editing = False
|
||||
|
||||
@@ -407,10 +297,11 @@ class FinishEditingStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
data = props.get_props_kwargs(convert_to_project_units=True)
|
||||
props.is_editing = False
|
||||
update_stair_modifier(context)
|
||||
regenerate_stair_mesh(context)
|
||||
update_stair_representation(obj)
|
||||
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Stair")
|
||||
data = json.dumps(data)
|
||||
data = tool.Ifc.get().createIfcText(json.dumps(data))
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": data})
|
||||
|
||||
# update IfcStairFlight properties
|
||||
|
||||
@@ -169,7 +169,7 @@ class UpdateDataFromSverchok(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=pset,
|
||||
properties={"Data": json.dumps(sverchok_data)},
|
||||
properties={"Data": tool.Ifc.get().createIfcText(json.dumps(sverchok_data))},
|
||||
)
|
||||
update_sverchok_modifier(context)
|
||||
|
||||
|
||||
@@ -30,12 +30,13 @@ from blenderbim.bim.module.model.data import (
|
||||
RoofData,
|
||||
)
|
||||
from blenderbim.bim.module.model.prop import get_ifc_class
|
||||
from blenderbim.bim.module.model.stair import update_stair_modifier
|
||||
from blenderbim.bim.module.model.stair import regenerate_stair_mesh
|
||||
from blenderbim.bim.module.model.window import update_window_modifier_bmesh
|
||||
from blenderbim.bim.module.model.door import update_door_modifier_bmesh
|
||||
from blenderbim.bim.module.model.railing import update_railing_modifier_bmesh
|
||||
from blenderbim.bim.module.model.roof import update_roof_modifier_bmesh
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
from collections.abc import Iterable
|
||||
|
||||
|
||||
class LaunchTypeManager(bpy.types.Operator):
|
||||
@@ -160,7 +161,7 @@ class BIM_PT_Grids(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.row().operator("mesh.add_grid", icon="ADD", text="Add Grids")
|
||||
@@ -259,35 +260,42 @@ class BIM_PT_stair(bpy.types.Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Stair parameters", icon="IPO_CONSTANT")
|
||||
|
||||
stair_data = StairData.data["pset_data"]["data_dict"]
|
||||
if props.is_editing:
|
||||
calculated_params = tool.Model.get_active_stair_calculated_params()
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.finish_editing_stair", icon="CHECKMARK", text="Finish Editing")
|
||||
row.operator("bim.cancel_editing_stair", icon="CANCEL", text="")
|
||||
row = self.layout.row(align=True)
|
||||
for prop_name in props.get_props_kwargs():
|
||||
self.layout.prop(props, prop_name)
|
||||
update_stair_modifier(context)
|
||||
prop_value = getattr(props, prop_name)
|
||||
if isinstance(prop_value, Iterable) and not isinstance(prop_value, str):
|
||||
prop_readable_name = props.bl_rna.properties[prop_name].name
|
||||
self.layout.label(text=f"{prop_readable_name}:")
|
||||
self.layout.prop(props, prop_name, text="")
|
||||
else:
|
||||
self.layout.prop(props, prop_name)
|
||||
regenerate_stair_mesh(context)
|
||||
else:
|
||||
calculated_params = StairData.data["calculated_params"]
|
||||
row.operator("bim.enable_editing_stair", icon="GREASEPENCIL", text="")
|
||||
row.operator("bim.remove_stair", icon="X", text="")
|
||||
row = self.layout.row(align=True)
|
||||
for prop_name, prop_value in StairData.data["general_params"].items():
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=prop_name)
|
||||
row.label(text=str(prop_value))
|
||||
if isinstance(prop_value, Iterable) and not isinstance(prop_value, str):
|
||||
row.label(text=f"{prop_name}:")
|
||||
row = self.layout.row(align=True)
|
||||
for prop_value_item in prop_value:
|
||||
row.label(text=str(prop_value_item))
|
||||
else:
|
||||
row.label(text=prop_name)
|
||||
row.label(text=str(prop_value))
|
||||
|
||||
# calculated properties
|
||||
number_of_rises = props.number_of_treads + 1
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Number of risers")
|
||||
row.label(text=str(number_of_rises))
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Tread rise")
|
||||
row.label(text=str(round(props.height / number_of_rises, 5)))
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Length")
|
||||
row.label(text=str(round(props.tread_run * number_of_rises, 5)))
|
||||
for prop_name, prop_value in calculated_params.items():
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=prop_name)
|
||||
row.label(text=str(prop_value))
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Stair Found")
|
||||
|
||||
@@ -496,7 +496,7 @@ class AddWindow(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=pset,
|
||||
properties={"Data": json.dumps(window_data, default=list)},
|
||||
properties={"Data": tool.Ifc.get().createIfcText(json.dumps(window_data, default=list))},
|
||||
)
|
||||
update_window_modifier_representation(context, obj)
|
||||
return {"FINISHED"}
|
||||
@@ -553,7 +553,7 @@ class FinishEditingWindow(bpy.types.Operator, tool.Ifc.Operator):
|
||||
update_window_modifier_representation(context, obj)
|
||||
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Window")
|
||||
window_data = json.dumps(window_data, default=list)
|
||||
window_data = tool.Ifc.get().createIfcText(json.dumps(window_data, default=list))
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": window_data})
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -616,6 +616,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
usage = tool.Model.get_usage_type(element)
|
||||
if not usage:
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
representation = tool.Geometry.resolve_mapped_representation(representation)
|
||||
if representation and representation.RepresentationType == "SweptSolid":
|
||||
usage = "SWEPTSOLID"
|
||||
else:
|
||||
|
||||
@@ -83,7 +83,7 @@ class BIM_PT_people(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_stakeholders"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -142,7 +142,7 @@ class BIM_PT_organisations(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_stakeholders"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -189,7 +189,7 @@ class BIM_PT_owner(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_stakeholders"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -240,7 +240,7 @@ class BIM_PT_actor(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_stakeholders"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -27,11 +27,11 @@ from blenderbim.bim.module.profile.prop import generate_thumbnail_for_active_pro
|
||||
class BIM_PT_profiles(Panel):
|
||||
bl_label = "Profiles"
|
||||
bl_idname = "BIM_PT_profiles"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_geometry"
|
||||
bl_parent_id = "BIM_PT_tab_profiles"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -58,11 +58,11 @@ class BIM_PT_profiles(Panel):
|
||||
box.template_icon(icon_value=preview_image.icon_id, scale=5)
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{ProfileData.data['total_profiles']} Named Profiles Found", icon="SNAP_GRID")
|
||||
row.label(text=f"{ProfileData.data['total_profiles']} Named Profiles", icon="ITALIC")
|
||||
if self.props.is_editing:
|
||||
row.operator("bim.disable_profile_editing_ui", text="", icon="CANCEL")
|
||||
else:
|
||||
row.operator("bim.load_profiles", text="", icon="GREASEPENCIL")
|
||||
row.operator("bim.load_profiles", text="", icon="IMPORT")
|
||||
|
||||
if not self.props.is_editing:
|
||||
return
|
||||
|
||||
@@ -1115,8 +1115,8 @@ class ExportIFC(bpy.types.Operator):
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
if properties.should_save_as:
|
||||
return "Export the IFC project to a selected file"
|
||||
return "Export the IFC project to this file"
|
||||
return "Save the IFC file under a new name, or relocate file"
|
||||
return "Save the IFC file. Will save both .IFC/.BLEND files if synced together"
|
||||
|
||||
|
||||
class ImportIFC(bpy.types.Operator):
|
||||
|
||||
@@ -84,7 +84,7 @@ class BIM_PT_project(Panel):
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
bl_parent_id = "BIM_PT_project_info"
|
||||
bl_parent_id = "BIM_PT_tab_project_info"
|
||||
|
||||
def draw(self, context):
|
||||
if not ProjectData.is_loaded:
|
||||
@@ -244,7 +244,7 @@ class BIM_PT_project_library(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.use_property_decorate = False
|
||||
@@ -287,7 +287,7 @@ class BIM_PT_links(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMProjectProperties
|
||||
|
||||
@@ -29,7 +29,7 @@ class BIM_PT_pset_template(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
def draw(self, context):
|
||||
if not PsetTemplatesData.is_loaded:
|
||||
|
||||
@@ -79,7 +79,8 @@ def calculate_formwork_area(objs, context):
|
||||
modifier = copied_obj.modifiers.new(type="BOOLEAN", name="Boolean")
|
||||
modifier.operation = "UNION"
|
||||
modifier.object = obj
|
||||
bpy.ops.object.modifier_apply({"object": copied_obj}, modifier="Boolean")
|
||||
with context.temp_override(object=copied_obj):
|
||||
bpy.ops.object.modifier_apply(modifier="Boolean")
|
||||
|
||||
copied_obj.name = "Formwork"
|
||||
copied_obj.BIMObjectProperties.ifc_definition_id = 0
|
||||
|
||||
@@ -66,6 +66,7 @@ class EnableReassignClass(bpy.types.Operator):
|
||||
context.scene.BIMRootProperties.ifc_product = ifc_product
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
context.scene.BIMRootProperties.ifc_class = element.is_a()
|
||||
context.scene.BIMRootProperties.relating_class_object = None
|
||||
if hasattr(element, "PredefinedType") and element.PredefinedType:
|
||||
context.scene.BIMRootProperties.ifc_predefined_type = element.PredefinedType
|
||||
return {"FINISHED"}
|
||||
@@ -91,7 +92,10 @@ class ReassignClass(bpy.types.Operator):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
objects = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
|
||||
if self.obj:
|
||||
objects = [bpy.data.objects.get(self.obj)]
|
||||
else:
|
||||
objects = set(context.selected_objects + [context.active_object])
|
||||
self.file = IfcStore.get_file()
|
||||
predefined_type = context.scene.BIMRootProperties.ifc_predefined_type
|
||||
if predefined_type == "USERDEFINED":
|
||||
@@ -100,14 +104,12 @@ class ReassignClass(bpy.types.Operator):
|
||||
product = ifcopenshell.api.run(
|
||||
"root.reassign_class",
|
||||
self.file,
|
||||
**{
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"ifc_class": context.scene.BIMRootProperties.ifc_class,
|
||||
"predefined_type": predefined_type,
|
||||
},
|
||||
product=self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
ifc_class=context.scene.BIMRootProperties.ifc_class,
|
||||
predefined_type=predefined_type,
|
||||
)
|
||||
obj.name = "{}/{}".format(product.is_a(), getattr(product, "Name", "None"))
|
||||
IfcStore.link_element(product, obj)
|
||||
tool.Ifc.link(product, obj)
|
||||
obj.BIMObjectProperties.is_reassigning_class = False
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -174,15 +176,21 @@ class UnlinkObject(bpy.types.Operator):
|
||||
if element:
|
||||
if self.should_delete:
|
||||
obj_copy = obj.copy()
|
||||
|
||||
# copy object data, so it won't be removed by `delete_ifc_object`
|
||||
if obj.data:
|
||||
obj_copy.data = obj.data.copy()
|
||||
if obj.type == "MESH":
|
||||
obj_copy.data.BIMMeshProperties.ifc_definition_id = 0
|
||||
|
||||
for collection in obj.users_collection:
|
||||
collection.objects.link(obj_copy)
|
||||
tool.Geometry.delete_ifc_object(obj)
|
||||
obj = obj_copy
|
||||
if obj in IfcStore.edited_objs:
|
||||
IfcStore.edited_objs.remove(obj)
|
||||
IfcStore.unlink_element(obj=obj)
|
||||
if obj.data:
|
||||
obj.data = obj.data.copy()
|
||||
tool.Ifc.unlink(obj=obj)
|
||||
|
||||
for material_slot in obj.material_slots:
|
||||
if material_slot.material:
|
||||
material_slot.material = material_slot.material.copy()
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.schema
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.module.root.data import IfcClassData
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -80,12 +81,46 @@ def get_contexts(self, context):
|
||||
return IfcClassData.data["contexts"]
|
||||
|
||||
|
||||
def update_relating_class_from_object(self, context):
|
||||
if self.relating_class_object is None:
|
||||
return
|
||||
element = tool.Ifc.get_entity(self.relating_class_object)
|
||||
if not element:
|
||||
return
|
||||
self.ifc_class = element.is_a()
|
||||
predefined_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
if predefined_type:
|
||||
if element.PredefinedType == "USERDEFINED":
|
||||
self.ifc_predefined_type = "USERDEFINED"
|
||||
self.ifc_userdefined_type = predefined_type
|
||||
else:
|
||||
self.ifc_predefined_type = predefined_type
|
||||
bpy.ops.bim.reassign_class()
|
||||
|
||||
|
||||
def is_object_class_applicable(self, obj):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return False
|
||||
active_element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if not active_element:
|
||||
return False
|
||||
return element.is_a("IfcTypeObject") == active_element.is_a("IfcTypeObject")
|
||||
|
||||
|
||||
class BIMRootProperties(PropertyGroup):
|
||||
contexts: EnumProperty(items=get_contexts, name="Contexts")
|
||||
ifc_product: EnumProperty(items=get_ifc_products, name="Products", update=refresh_classes)
|
||||
ifc_class: EnumProperty(items=get_ifc_classes, name="Class", update=refresh_predefined_types)
|
||||
ifc_predefined_type: EnumProperty(items=get_ifc_predefined_types, name="Predefined Type", default=None)
|
||||
ifc_userdefined_type: StringProperty(name="Userdefined Type")
|
||||
relating_class_object: PointerProperty(
|
||||
type=bpy.types.Object,
|
||||
name="Copy Class",
|
||||
update=update_relating_class_from_object,
|
||||
poll=is_object_class_applicable,
|
||||
description="Copy the selected object's class and predefined type to the active object",
|
||||
)
|
||||
|
||||
getter_enum_suggestions = {
|
||||
"ifc_class": get_ifc_classes_suggestions,
|
||||
|
||||
@@ -58,6 +58,7 @@ class BIM_PT_class(Panel):
|
||||
root_prop.get_ifc_predefined_types(context.scene.BIMRootProperties, context),
|
||||
is_reassigning_class=True,
|
||||
)
|
||||
self.layout.prop(context.scene.BIMRootProperties, "relating_class_object", icon="COPYDOWN")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=IfcClassData.data["name"])
|
||||
|
||||
@@ -57,6 +57,25 @@ def updateContainerName(self, context):
|
||||
props.container_name = self.name
|
||||
|
||||
|
||||
def update_relating_container_from_object(self, context):
|
||||
if self.relating_container_object is None or context.active_object is None:
|
||||
return
|
||||
element = tool.Ifc.get_entity(self.relating_container_object)
|
||||
if not element:
|
||||
return
|
||||
container = ifcopenshell.util.element.get_container(element)
|
||||
if container:
|
||||
bpy.ops.bim.assign_container(structure=container.id())
|
||||
else:
|
||||
bpy.ops.bim.disable_editing_container()
|
||||
bpy.ops.bim.remove_container()
|
||||
|
||||
|
||||
def is_object_applicable(self, obj):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
return bool(element)
|
||||
|
||||
|
||||
class SpatialElement(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
long_name: StringProperty(name="Long Name")
|
||||
@@ -73,6 +92,13 @@ class BIMSpatialProperties(PropertyGroup):
|
||||
|
||||
class BIMObjectSpatialProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
relating_container_object: PointerProperty(
|
||||
type=bpy.types.Object,
|
||||
name="Copy Container",
|
||||
update=update_relating_container_from_object,
|
||||
poll=is_object_applicable,
|
||||
description="Copy the target object's container to the active object",
|
||||
)
|
||||
|
||||
|
||||
class BIMContainer(PropertyGroup):
|
||||
|
||||
@@ -62,6 +62,7 @@ class BIM_PT_spatial(Panel):
|
||||
row.operator("bim.disable_editing_container", icon="CANCEL", text="")
|
||||
|
||||
self.layout.template_list("BIM_UL_containers", "", props, "containers", props, "active_container_index")
|
||||
self.layout.prop(osprops, "relating_container_object")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
if SpatialData.data["label"]:
|
||||
@@ -103,7 +104,7 @@ class BIM_PT_SpatialManager(Panel):
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -25,14 +25,14 @@ classes = (
|
||||
operator.AddStyle,
|
||||
operator.BrowseExternalStyle,
|
||||
operator.ClearTextureMapPath,
|
||||
operator.DisableAddPresentationStyle,
|
||||
operator.DisableAddingPresentationStyle,
|
||||
operator.DisableEditingExternalStyle,
|
||||
operator.DisableEditingStyle,
|
||||
operator.DisableEditingStyles,
|
||||
operator.EditExternalStyle,
|
||||
operator.EditStyle,
|
||||
operator.EditSurfaceStyle,
|
||||
operator.EnableAddPresentationStyle,
|
||||
operator.EnableAddingPresentationStyle,
|
||||
operator.EnableEditingExternalStyle,
|
||||
operator.EnableEditingStyle,
|
||||
operator.EnableEditingSurfaceStyle,
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
from ifcopenshell.util.doc import get_entity_doc
|
||||
import blenderbim.tool as tool
|
||||
from ifcopenshell.util.doc import get_entity_doc
|
||||
|
||||
|
||||
def refresh():
|
||||
@@ -33,9 +33,18 @@ class StylesData:
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {"style_types": cls.style_types(), "total_styles": cls.total_styles()}
|
||||
cls.data = {
|
||||
"style_types": cls.style_types(),
|
||||
"total_styles": cls.total_styles(),
|
||||
"reflectance_methods": cls.reflectance_methods(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def reflectance_methods(cls):
|
||||
declaration = tool.Ifc.schema().declaration_by_name("IfcReflectanceMethodEnum")
|
||||
return [(i, i, "") for i in declaration.enumeration_items()]
|
||||
|
||||
@classmethod
|
||||
def style_types(cls):
|
||||
declaration = tool.Ifc.schema().declaration_by_name("IfcPresentationStyle")
|
||||
|
||||
@@ -431,8 +431,8 @@ class ClearTextureMapPath(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableAddPresentationStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_add_presentation_style"
|
||||
class EnableAddingPresentationStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_adding_presentation_style"
|
||||
bl_label = "Enable Add Presentation Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@@ -441,8 +441,8 @@ class EnableAddPresentationStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props.is_adding = True
|
||||
|
||||
|
||||
class DisableAddPresentationStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_add_presentation_style"
|
||||
class DisableAddingPresentationStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_adding_presentation_style"
|
||||
bl_label = "Disable Add Presentation Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@@ -477,6 +477,13 @@ class AddPresentationStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
)
|
||||
if props.surface_style_class == "IfcSurfaceStyleRendering":
|
||||
surface_style.ReflectanceMethod = "NOTDEFINED"
|
||||
material = bpy.data.materials.new(style.Name)
|
||||
tool.Ifc.link(style, material)
|
||||
material.use_fake_user = True
|
||||
if surface_style.is_a("IfcSurfaceStyleShading"):
|
||||
tool.Loader.create_surface_style_shading(material, surface_style)
|
||||
elif surface_style.is_a("IfcSurfaceStyleRendering"):
|
||||
tool.Loader.create_surface_style_rendering(material, surface_style)
|
||||
props.is_adding = False
|
||||
core.load_styles(tool.Style, style_type=props.style_type)
|
||||
|
||||
@@ -492,20 +499,60 @@ class EnableEditingSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
style = tool.Ifc.get().by_id(self.style)
|
||||
|
||||
shading = None
|
||||
|
||||
surface_style = None
|
||||
for style2 in style.Styles:
|
||||
if style2.is_a() == self.ifc_class:
|
||||
surface_style = style2
|
||||
break
|
||||
if style2.is_a() == "IfcSurfaceStyleShading":
|
||||
shading = style2
|
||||
|
||||
color_to_tuple = lambda x: (x.Red, x.Green, x.Blue)
|
||||
|
||||
if surface_style:
|
||||
color_to_tuple = lambda x: (x.Red, x.Green, x.Blue)
|
||||
if self.ifc_class == "IfcSurfaceStyleShading":
|
||||
props.surface_colour = color_to_tuple(surface_style.SurfaceColour)
|
||||
props.transparency = surface_style.Transparency or 0.
|
||||
props.transparency = surface_style.Transparency or 0.0
|
||||
elif self.ifc_class == "IfcSurfaceStyleRendering":
|
||||
props.surface_colour = color_to_tuple(surface_style.SurfaceColour)
|
||||
props.transparency = surface_style.Transparency or 0.
|
||||
props.transparency = surface_style.Transparency or 0.0
|
||||
|
||||
if surface_style.DiffuseColour:
|
||||
props.is_diffuse_colour_null = False
|
||||
if surface_style.DiffuseColour.is_a("IfcColourRgb"):
|
||||
props.diffuse_colour_class = "IfcColourRgb"
|
||||
props.diffuse_colour = color_to_tuple(surface_style.DiffuseColour)
|
||||
else:
|
||||
props.diffuse_colour_class = "IfcNormalisedRatioMeasure"
|
||||
props.diffuse_colour_ratio = surface_style.DiffuseColour.wrappedValue
|
||||
else:
|
||||
props.is_diffuse_colour_null = False
|
||||
|
||||
if surface_style.SpecularColour:
|
||||
props.is_specular_colour_null = False
|
||||
if surface_style.SpecularColour.is_a("IfcColourRgb"):
|
||||
props.specular_colour_class = "IfcColourRgb"
|
||||
props.specular_colour = color_to_tuple(surface_style.SpecularColour)
|
||||
else:
|
||||
props.specular_colour_class = "IfcNormalisedRatioMeasure"
|
||||
props.specular_colour_ratio = surface_style.SpecularColour.wrappedValue
|
||||
else:
|
||||
props.is_specular_colour_null = False
|
||||
|
||||
if surface_style.SpecularHighlight:
|
||||
props.is_specular_highlight_null = False
|
||||
if surface_style.SpecularHighlight.is_a("IfcSpecularRoughness"):
|
||||
props.specular_highlight = surface_style.SpecularHighlight.wrappedValue
|
||||
else:
|
||||
props.is_specular_highlight_null = False # Exponent is meaningless
|
||||
else:
|
||||
props.is_specular_highlight_null = True
|
||||
|
||||
props.reflectance_method = surface_style.ReflectanceMethod
|
||||
elif shading:
|
||||
props.surface_colour = color_to_tuple(shading.SurfaceColour)
|
||||
props.transparency = shading.Transparency or 0.0
|
||||
|
||||
props.is_editing_style = self.style
|
||||
props.is_editing_class = self.ifc_class
|
||||
@@ -518,12 +565,12 @@ class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = bpy.context.scene.BIMStylesProperties
|
||||
style = tool.Ifc.get().by_id(self.props.is_editing_style)
|
||||
self.style = tool.Ifc.get().by_id(self.props.is_editing_style)
|
||||
|
||||
self.surface_style = None
|
||||
self.shading_style = None
|
||||
self.rendering_style = None
|
||||
for style2 in style.Styles:
|
||||
for style2 in self.style.Styles:
|
||||
if style2.is_a() == self.props.is_editing_class:
|
||||
self.surface_style = style2
|
||||
if style2.is_a() == "IfcSurfaceStyleShading":
|
||||
@@ -540,11 +587,82 @@ class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.load_styles(tool.Style, style_type=self.props.style_type)
|
||||
|
||||
def edit_existing_style(self):
|
||||
material = tool.Ifc.get_object(self.style)
|
||||
if self.surface_style.is_a() == "IfcSurfaceStyleShading":
|
||||
self.surface_style.SurfaceColour.Red = self.props.surface_colour[0]
|
||||
self.surface_style.SurfaceColour.Green = self.props.surface_colour[1]
|
||||
self.surface_style.SurfaceColour.Blue = self.props.surface_colour[2]
|
||||
self.surface_style.Transparency = self.props.transparency or None
|
||||
ifcopenshell.api.run(
|
||||
"style.edit_surface_style",
|
||||
tool.Ifc.get(),
|
||||
style=self.surface_style,
|
||||
attributes={
|
||||
"SurfaceColour": self.color_to_dict(self.props.surface_colour),
|
||||
"Transparency": self.props.transparency or None,
|
||||
},
|
||||
)
|
||||
tool.Loader.create_surface_style_shading(material, self.surface_style)
|
||||
elif self.surface_style.is_a() == "IfcSurfaceStyleRendering":
|
||||
ifcopenshell.api.run(
|
||||
"style.edit_surface_style",
|
||||
tool.Ifc.get(),
|
||||
style=self.surface_style,
|
||||
attributes=self.get_rendering_attributes(),
|
||||
)
|
||||
tool.Loader.create_surface_style_rendering(material, self.surface_style)
|
||||
|
||||
def add_new_style(self):
|
||||
pass
|
||||
material = tool.Ifc.get_object(self.style)
|
||||
if self.props.is_editing_class == "IfcSurfaceStyleShading":
|
||||
surface_style = ifcopenshell.api.run(
|
||||
"style.add_surface_style",
|
||||
tool.Ifc.get(),
|
||||
style=self.style,
|
||||
ifc_class="IfcSurfaceStyleShading",
|
||||
attributes=self.get_shading_attributes(),
|
||||
)
|
||||
tool.Loader.create_surface_style_shading(material, surface_style)
|
||||
elif self.props.is_editing_class == "IfcSurfaceStyleRendering":
|
||||
surface_style = ifcopenshell.api.run(
|
||||
"style.add_surface_style",
|
||||
tool.Ifc.get(),
|
||||
style=self.style,
|
||||
ifc_class="IfcSurfaceStyleRendering",
|
||||
attributes=self.get_rendering_attributes(),
|
||||
)
|
||||
tool.Loader.create_surface_style_rendering(material, surface_style)
|
||||
|
||||
def get_shading_attributes(self):
|
||||
return {
|
||||
"SurfaceColour": self.color_to_dict(self.props.surface_colour),
|
||||
"Transparency": self.props.transparency or None,
|
||||
}
|
||||
|
||||
def get_rendering_attributes(self):
|
||||
if self.props.is_diffuse_colour_null:
|
||||
diffuse_colour = None
|
||||
elif self.props.diffuse_colour_class == "IfcColourRgb":
|
||||
diffuse_colour = self.color_to_dict(self.props.diffuse_colour)
|
||||
elif self.props.diffuse_colour_class == "IfcNormalisedRatioMeasure":
|
||||
diffuse_colour = self.props.diffuse_colour_ratio
|
||||
|
||||
if self.props.is_specular_colour_null:
|
||||
specular_colour = None
|
||||
elif self.props.specular_colour_class == "IfcColourRgb":
|
||||
specular_colour = self.color_to_dict(self.props.specular_colour)
|
||||
elif self.props.specular_colour_class == "IfcNormalisedRatioMeasure":
|
||||
specular_colour = self.props.specular_colour_ratio
|
||||
|
||||
if self.props.is_specular_highlight_null:
|
||||
specular_highlight = None
|
||||
else:
|
||||
specular_highlight = {"SpecularRoughness": self.props.specular_highlight}
|
||||
|
||||
return {
|
||||
"SurfaceColour": self.color_to_dict(self.props.surface_colour),
|
||||
"Transparency": self.props.transparency or None,
|
||||
"ReflectanceMethod": self.props.reflectance_method,
|
||||
"DiffuseColour": diffuse_colour,
|
||||
"SpecularColour": specular_colour,
|
||||
"SpecularHighlight": specular_highlight,
|
||||
}
|
||||
|
||||
def color_to_dict(self, x):
|
||||
return {"Red": x[0], "Green": x[1], "Blue": x[2]}
|
||||
|
||||
@@ -40,6 +40,12 @@ def get_style_types(self, context):
|
||||
return StylesData.data["style_types"]
|
||||
|
||||
|
||||
def get_reflectance_methods(self, context):
|
||||
if not StylesData.is_loaded:
|
||||
StylesData.load()
|
||||
return StylesData.data["reflectance_methods"]
|
||||
|
||||
|
||||
class Style(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
@@ -49,6 +55,10 @@ class Style(PropertyGroup):
|
||||
surface_colour: bpy.props.FloatVectorProperty(
|
||||
name="Surface Colour", subtype="COLOR", default=(1, 1, 1), min=0.0, max=1.0, size=3
|
||||
)
|
||||
has_diffuse_colour: BoolProperty(name="Has Diffuse Colour", default=False)
|
||||
diffuse_colour: bpy.props.FloatVectorProperty(
|
||||
name="Diffuse Colour", subtype="COLOR", default=(1, 1, 1), min=0.0, max=1.0, size=3
|
||||
)
|
||||
|
||||
|
||||
STYLE_TYPES = [
|
||||
@@ -91,6 +101,27 @@ class BIMStylesProperties(PropertyGroup):
|
||||
name="Surface Colour", subtype="COLOR", default=(1, 1, 1), min=0.0, max=1.0, size=3
|
||||
)
|
||||
transparency: bpy.props.FloatProperty(name="Transparency", default=0.0, min=0.0, max=1.0)
|
||||
is_diffuse_colour_null: BoolProperty(name="Is Null")
|
||||
diffuse_colour_class: EnumProperty(
|
||||
items=[(x, x, "") for x in ("IfcColourRgb", "IfcNormalisedRatioMeasure")],
|
||||
name="Diffuse Colour Class",
|
||||
)
|
||||
diffuse_colour: bpy.props.FloatVectorProperty(
|
||||
name="Diffuse Colour", subtype="COLOR", default=(1, 1, 1), min=0.0, max=1.0, size=3
|
||||
)
|
||||
diffuse_colour_ratio: bpy.props.FloatProperty(name="Diffuse Ratio", default=0.0, min=0.0, max=1.0)
|
||||
is_specular_colour_null: BoolProperty(name="Is Null")
|
||||
specular_colour_class: EnumProperty(
|
||||
items=[(x, x, "") for x in ("IfcColourRgb", "IfcNormalisedRatioMeasure")],
|
||||
name="Specular Colour Class",
|
||||
)
|
||||
specular_colour: bpy.props.FloatVectorProperty(
|
||||
name="Specular Colour", subtype="COLOR", default=(1, 1, 1), min=0.0, max=1.0, size=3
|
||||
)
|
||||
specular_colour_ratio: bpy.props.FloatProperty(name="Specular Ratio", default=0.0, min=0.0, max=1.0)
|
||||
is_specular_highlight_null: BoolProperty(name="Is Null")
|
||||
specular_highlight: bpy.props.FloatProperty(name="Specular Highlight", default=0.0, min=0.0, max=1.0)
|
||||
reflectance_method: EnumProperty(name="Reflectance Method", items=get_reflectance_methods)
|
||||
styles: CollectionProperty(name="Styles", type=Style)
|
||||
active_style_index: IntProperty(name="Active Style Index")
|
||||
active_style_type: EnumProperty(
|
||||
|
||||
@@ -48,7 +48,7 @@ class BIM_PT_styles(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} {}s".format(len(self.props.styles), self.props.style_type), icon="SHADING_RENDERED")
|
||||
if not self.props.is_adding:
|
||||
row.operator("bim.enable_add_presentation_style", text="", icon="ADD")
|
||||
row.operator("bim.enable_adding_presentation_style", text="", icon="ADD")
|
||||
row.operator("bim.disable_editing_styles", text="", icon="CANCEL")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -69,7 +69,7 @@ class BIM_PT_styles(Panel):
|
||||
row.prop(self.props, "surface_colour", text="Colour")
|
||||
row = box.row(align=True)
|
||||
row.operator("bim.add_presentation_style", text="Save New Style", icon="CHECKMARK")
|
||||
row.operator("bim.disable_add_presentation_style", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_adding_presentation_style", text="", icon="CANCEL")
|
||||
|
||||
if self.props.styles and self.props.active_style_index < len(self.props.styles):
|
||||
row = self.layout.row(align=True)
|
||||
@@ -131,6 +131,55 @@ class BIM_PT_styles(Panel):
|
||||
row.prop(self.props, "surface_colour")
|
||||
row = self.layout.row()
|
||||
row.prop(self.props, "transparency")
|
||||
row = self.layout.row()
|
||||
row.prop(self.props, "reflectance_method")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Diffuse")
|
||||
row.prop(self.props, "diffuse_colour_class", text="")
|
||||
if self.props.diffuse_colour_class == "IfcColourRgb":
|
||||
row.prop(self.props, "diffuse_colour", text="")
|
||||
else:
|
||||
row.prop(self.props, "diffuse_colour_ratio", text="")
|
||||
row.prop(
|
||||
self.props,
|
||||
"is_diffuse_colour_null",
|
||||
text="",
|
||||
icon="RADIOBUT_OFF" if self.props.is_diffuse_colour_null else "RADIOBUT_ON",
|
||||
)
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
if self.props.reflectance_method in ("PHYSICAL", "NOTDEFINED"):
|
||||
row.label(text="Metallic")
|
||||
else:
|
||||
row.label(text="Specular")
|
||||
row.prop(self.props, "specular_colour_class", text="")
|
||||
if self.props.specular_colour_class == "IfcColourRgb":
|
||||
row.prop(self.props, "specular_colour", text="")
|
||||
else:
|
||||
row.prop(self.props, "specular_colour_ratio", text="")
|
||||
row.prop(
|
||||
self.props,
|
||||
"is_specular_colour_null",
|
||||
text="",
|
||||
icon="RADIOBUT_OFF" if self.props.is_specular_colour_null else "RADIOBUT_ON",
|
||||
)
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
if self.props.reflectance_method in ("PHYSICAL", "NOTDEFINED"):
|
||||
row.label(text="Roughness")
|
||||
elif self.props.reflectance_method in ("PHONG"):
|
||||
row.label(text="Shininess")
|
||||
else:
|
||||
row.label(text="Highlight")
|
||||
row.prop(self.props, "specular_highlight", text="")
|
||||
row.prop(
|
||||
self.props,
|
||||
"is_specular_highlight_null",
|
||||
text="",
|
||||
icon="RADIOBUT_OFF" if self.props.is_specular_highlight_null else "RADIOBUT_ON",
|
||||
)
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_surface_style", text="Save Rendering Style", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_style", text="", icon="CANCEL")
|
||||
@@ -285,9 +334,11 @@ class BIM_UL_styles(UIList):
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.name)
|
||||
if item.has_surface_colour:
|
||||
row = row.row()
|
||||
row = row.row(align=True)
|
||||
row.enabled = False
|
||||
row.prop(item, "surface_colour", text="")
|
||||
if item.has_diffuse_colour:
|
||||
row.prop(item, "diffuse_colour", text="")
|
||||
row2 = row.row()
|
||||
row2.alignment = "RIGHT"
|
||||
row2.label(text=str(item.total_elements))
|
||||
|
||||
@@ -22,38 +22,52 @@ from . import ui, prop, operator, decorator
|
||||
classes = (
|
||||
operator.AddPort,
|
||||
operator.AddSystem,
|
||||
operator.AddZone,
|
||||
operator.AssignSystem,
|
||||
operator.AssignUnassignFlowControl,
|
||||
operator.ConnectPort,
|
||||
operator.DisableEditingSystem,
|
||||
operator.DisableEditingZone,
|
||||
operator.DisableSystemEditingUI,
|
||||
operator.DisconnectPort,
|
||||
operator.EditSystem,
|
||||
operator.EditZone,
|
||||
operator.EnableEditingSystem,
|
||||
operator.EnableEditingZone,
|
||||
operator.HidePorts,
|
||||
operator.LoadSystems,
|
||||
operator.LoadZones,
|
||||
operator.MEPConnectElements,
|
||||
operator.RemovePort,
|
||||
operator.RemoveSystem,
|
||||
operator.RemoveZone,
|
||||
operator.SelectSystemProducts,
|
||||
operator.MEPConnectElements,
|
||||
operator.SetFlowDirection,
|
||||
operator.ShowPorts,
|
||||
operator.UnassignSystem,
|
||||
operator.UnloadZones,
|
||||
prop.System,
|
||||
prop.Zone,
|
||||
prop.BIMSystemProperties,
|
||||
prop.BIMZoneProperties,
|
||||
ui.BIM_PT_systems,
|
||||
ui.BIM_PT_object_systems,
|
||||
ui.BIM_PT_zones,
|
||||
ui.BIM_PT_active_object_zones,
|
||||
ui.BIM_PT_ports,
|
||||
ui.BIM_PT_port,
|
||||
ui.BIM_PT_flow_controls,
|
||||
ui.BIM_UL_systems,
|
||||
ui.BIM_UL_object_systems,
|
||||
ui.BIM_UL_zones,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMSystemProperties = bpy.props.PointerProperty(type=prop.BIMSystemProperties)
|
||||
bpy.types.Scene.BIMZoneProperties = bpy.props.PointerProperty(type=prop.BIMZoneProperties)
|
||||
bpy.app.handlers.load_post.append(decorator.toggle_decorations_on_load)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMSystemProperties
|
||||
del bpy.types.Scene.BIMZoneProperties
|
||||
bpy.app.handlers.load_post.remove(decorator.toggle_decorations_on_load)
|
||||
|
||||
@@ -25,6 +25,8 @@ import blenderbim.tool as tool
|
||||
|
||||
def refresh():
|
||||
SystemData.is_loaded = False
|
||||
ZonesData.is_loaded = False
|
||||
ActiveObjectZonesData.is_loaded = False
|
||||
ObjectSystemData.is_loaded = False
|
||||
PortData.is_loaded = False
|
||||
SystemDecorationData.is_loaded = False
|
||||
@@ -47,6 +49,7 @@ class SystemData:
|
||||
declaration = tool.Ifc.schema().declaration_by_name("IfcSystem")
|
||||
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||
version = tool.Ifc.get_schema()
|
||||
|
||||
# We're only interested in systems for services. Not sure why IFC groups these together.
|
||||
return [
|
||||
(c, c, get_entity_doc(version, c).get("description", ""))
|
||||
@@ -56,7 +59,7 @@ class SystemData:
|
||||
|
||||
@classmethod
|
||||
def total_systems(cls):
|
||||
return len(tool.Ifc.get().by_type("IfcSystem"))
|
||||
return len(tool.System.get_systems())
|
||||
|
||||
|
||||
class ObjectSystemData:
|
||||
@@ -67,9 +70,9 @@ class ObjectSystemData:
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"systems": cls.systems(),
|
||||
"total_systems": cls.total_systems(),
|
||||
# AFTER SYSTEMS
|
||||
"connected_elements": cls.connected_elements(),
|
||||
"flow_controls_data": cls.flow_controls_data(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@@ -83,16 +86,32 @@ class ObjectSystemData:
|
||||
results.append({"id": system.id(), "name": system.Name or "Unnamed", "ifc_class": system.is_a()})
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def total_systems(cls):
|
||||
return len(tool.Ifc.get().by_type("IfcSystem"))
|
||||
|
||||
@classmethod
|
||||
def connected_elements(cls):
|
||||
if not cls.element:
|
||||
return set()
|
||||
return tool.System.get_connected_elements(cls.element)
|
||||
|
||||
@classmethod
|
||||
def flow_controls_data(cls):
|
||||
flow_controls_data = {}
|
||||
if not cls.element or not (
|
||||
cls.element.is_a("IfcDistributionControlElement") or cls.element.is_a("IfcDistributionFlowElement")
|
||||
):
|
||||
return flow_controls_data
|
||||
|
||||
if cls.element.is_a("IfcDistributionControlElement"):
|
||||
flow_controls_data["type"] = "IfcDistributionControlElement"
|
||||
flow_element = tool.System.get_flow_control_flow_element(cls.element)
|
||||
flow_element_obj = tool.Ifc.get_object(flow_element).name if flow_element else None
|
||||
flow_controls_data["flow_element"] = flow_element, flow_element_obj
|
||||
else:
|
||||
flow_controls_data["type"] = "IfcDistributionFlowElement"
|
||||
controls = [(c, tool.Ifc.get_object(c).name) for c in tool.System.get_flow_element_controls(cls.element)]
|
||||
flow_controls_data["controls"] = controls
|
||||
|
||||
return flow_controls_data
|
||||
|
||||
|
||||
class PortData:
|
||||
data = {}
|
||||
@@ -107,8 +126,8 @@ class PortData:
|
||||
"total_ports": cls.total_ports(),
|
||||
"located_ports_data": cls.located_ports_data(),
|
||||
"is_port": is_port,
|
||||
"port_connected_object": cls.port_connected_object() if is_port else None,
|
||||
"port_relating_object": cls.port_relating_object() if is_port else None,
|
||||
"port_connected_object_name": cls.port_connected_object_name() if is_port else None,
|
||||
"port_relating_object_name": cls.port_relating_object_name() if is_port else None,
|
||||
}
|
||||
# AFTER located_ports_data
|
||||
cls.data["selected_objects_flow_direction"] = cls.selected_objects_flow_direction() if not is_port else None
|
||||
@@ -123,16 +142,16 @@ class PortData:
|
||||
return cls.element and cls.element.is_a("IfcDistributionPort")
|
||||
|
||||
@classmethod
|
||||
def port_relating_object(cls):
|
||||
return tool.Ifc.get_object(tool.System.get_port_relating_element(cls.element))
|
||||
def port_relating_object_name(cls):
|
||||
return tool.Ifc.get_object(tool.System.get_port_relating_element(cls.element)).name
|
||||
|
||||
@classmethod
|
||||
def port_connected_object(cls):
|
||||
def port_connected_object_name(cls):
|
||||
connected_port = tool.System.get_connected_port(cls.element)
|
||||
if not connected_port:
|
||||
return
|
||||
connected_element = tool.System.get_port_relating_element(connected_port)
|
||||
return tool.Ifc.get_object(connected_element)
|
||||
return tool.Ifc.get_object(connected_element).name
|
||||
|
||||
@classmethod
|
||||
def located_ports_data(cls):
|
||||
@@ -140,19 +159,23 @@ class PortData:
|
||||
|
||||
data = []
|
||||
for port in ports:
|
||||
port_obj = tool.Ifc.get_object(port)
|
||||
# port may be not present as a scene object
|
||||
port_obj_name = getattr(tool.Ifc.get_object(port), "name", None)
|
||||
connected_port = tool.System.get_connected_port(port)
|
||||
if connected_port:
|
||||
connected_obj = tool.Ifc.get_object(tool.System.get_port_relating_element(connected_port))
|
||||
connected_obj_name = tool.Ifc.get_object(tool.System.get_port_relating_element(connected_port)).name
|
||||
else:
|
||||
connected_obj = None
|
||||
connected_obj_name = None
|
||||
|
||||
data.append((port, port_obj, connected_obj))
|
||||
data.append((port, port_obj_name, connected_obj_name))
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def selected_objects_flow_direction(cls):
|
||||
for port, port_obj, connected_obj in cls.data["located_ports_data"]:
|
||||
for port, _, connected_obj_name in cls.data["located_ports_data"]:
|
||||
if connected_obj_name is None:
|
||||
continue
|
||||
connected_obj = bpy.data.objects[connected_obj_name]
|
||||
if connected_obj in bpy.context.selected_objects:
|
||||
return port.FlowDirection
|
||||
|
||||
@@ -164,7 +187,9 @@ class SystemDecorationData:
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {}
|
||||
cls.data = {
|
||||
"decorated_elements": cls.decorated_elements(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
cls.elements_ports_positions = {}
|
||||
|
||||
@@ -190,3 +215,55 @@ class SystemDecorationData:
|
||||
ports_data.append(port_data)
|
||||
cls.elements_ports_positions[element] = ports_data
|
||||
return cls.elements_ports_positions[element]
|
||||
|
||||
@classmethod
|
||||
def decorated_elements(cls):
|
||||
if not ObjectSystemData.is_loaded:
|
||||
ObjectSystemData.load()
|
||||
|
||||
# Priority:
|
||||
# 1. currently selected systems
|
||||
# 2. active system
|
||||
# 3. if previous steps didn't worked - decorate connected elements
|
||||
|
||||
decorated_elements = set()
|
||||
if ObjectSystemData.data["systems"]:
|
||||
for system in ObjectSystemData.data["systems"]:
|
||||
system = tool.Ifc.get().by_id(system["id"])
|
||||
decorated_elements.update(ifcopenshell.util.system.get_system_elements(system))
|
||||
elif active_system := tool.System.get_active_system():
|
||||
decorated_elements = set(ifcopenshell.util.system.get_system_elements(active_system))
|
||||
|
||||
if not decorated_elements:
|
||||
decorated_elements.update(ObjectSystemData.data["connected_elements"])
|
||||
|
||||
return decorated_elements
|
||||
|
||||
|
||||
class ZonesData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {"total_zones": cls.total_zones()}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def total_zones(cls):
|
||||
return len(tool.Ifc.get().by_type("IfcZone"))
|
||||
|
||||
|
||||
class ActiveObjectZonesData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {"zones": cls.zones()}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def zones(cls):
|
||||
systems = ifcopenshell.util.system.get_element_systems(tool.Ifc.get_entity(bpy.context.active_object))
|
||||
return [s.Name or "Unnamed" for s in systems if s.is_a("IfcZone")]
|
||||
|
||||
@@ -122,15 +122,15 @@ class SystemDecorator:
|
||||
selected_vertices = decoration_data["selected_vertices"]
|
||||
|
||||
### Actually drawing
|
||||
# 3D_POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
|
||||
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind()
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
# general shader
|
||||
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
self.shader.bind()
|
||||
|
||||
self.draw_batch("LINES", all_vertices, transparent_color(unselected_elements_color), unselected_edges)
|
||||
|
||||
@@ -21,6 +21,7 @@ import ifcopenshell.api
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.system as core
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.bim.helper
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.system.data import PortData
|
||||
from mathutils import Matrix
|
||||
@@ -67,7 +68,7 @@ class EditSystem(bpy.types.Operator, Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
core.edit_system(
|
||||
tool.Ifc, tool.System, system=tool.Ifc.get().by_id(context.scene.BIMSystemProperties.active_system_id)
|
||||
tool.Ifc, tool.System, system=tool.Ifc.get().by_id(context.scene.BIMSystemProperties.edited_system_id)
|
||||
)
|
||||
|
||||
|
||||
@@ -326,3 +327,161 @@ class SetFlowDirection(bpy.types.Operator, Operator):
|
||||
|
||||
self.report({"ERROR"}, "Selected elements are not connected to set the flow direction")
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
class LoadZones(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.load_zones"
|
||||
bl_label = "Load Zones"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMZoneProperties
|
||||
props.zones.clear()
|
||||
for zone in tool.Ifc.get().by_type("IfcZone"):
|
||||
new = props.zones.add()
|
||||
new.ifc_definition_id = zone.id()
|
||||
new.name = zone.Name or "Unnamed"
|
||||
props.is_loaded = True
|
||||
props.is_editing = 0
|
||||
|
||||
|
||||
class UnloadZones(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.unload_zones"
|
||||
bl_label = "Unload Zones"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMZoneProperties
|
||||
props.is_loaded = False
|
||||
|
||||
|
||||
class AddZone(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_zone"
|
||||
bl_label = "Add Zone"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
row = self.layout
|
||||
row.prop(self, "name", text="Name")
|
||||
|
||||
def _execute(self, context):
|
||||
element = ifcopenshell.api.run("system.add_system", tool.Ifc.get(), ifc_class="IfcZone")
|
||||
if self.name:
|
||||
element.Name = self.name
|
||||
bpy.ops.bim.load_zones()
|
||||
|
||||
|
||||
class EnableEditingZone(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.enable_editing_zone"
|
||||
bl_label = "Enable Editing Zone"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
zone: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMZoneProperties
|
||||
props.attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.zone), props.attributes)
|
||||
props.is_editing = self.zone
|
||||
|
||||
|
||||
class DisableEditingZone(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.disable_editing_zone"
|
||||
bl_label = "Disable Editing Zone"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMZoneProperties
|
||||
props.is_editing = 0
|
||||
|
||||
|
||||
class EditZone(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.edit_zone"
|
||||
bl_label = "Edit Zone"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMZoneProperties
|
||||
zone = tool.Ifc.get().by_id(props.is_editing)
|
||||
attributes = blenderbim.bim.helper.export_attributes(props.attributes)
|
||||
ifcopenshell.api.run("system.edit_system", tool.Ifc.get(), system=zone, attributes=attributes)
|
||||
props.is_editing = 0
|
||||
bpy.ops.bim.load_zones()
|
||||
|
||||
|
||||
class RemoveZone(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.remove_zone"
|
||||
bl_label = "Remove Zone"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
zone: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
ifcopenshell.api.run("system.remove_system", tool.Ifc.get(), system=tool.Ifc.get().by_id(self.zone))
|
||||
bpy.ops.bim.load_zones()
|
||||
|
||||
|
||||
class AssignUnassignFlowControl(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.assign_unassign_flow_control"
|
||||
bl_label = "Assign/Unassign Flow Control"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
flow_element: bpy.props.IntProperty(options={"SKIP_SAVE"})
|
||||
flow_control: bpy.props.IntProperty(options={"SKIP_SAVE"})
|
||||
assign: bpy.props.BoolProperty(name="Assign/Unassign", default=True, options={"SKIP_SAVE"})
|
||||
|
||||
def _execute(self, context):
|
||||
ifc_file = tool.Ifc.get()
|
||||
flow_element = None
|
||||
flow_controls = []
|
||||
from_selected_objects = False
|
||||
|
||||
if self.flow_element != 0:
|
||||
flow_element = ifc_file.by_id(self.flow_element)
|
||||
if self.flow_control != 0:
|
||||
flow_controls = [ifc_file.by_id(self.flow_control)]
|
||||
|
||||
# if not provided as arguments tried to get them from
|
||||
# the selected objects
|
||||
if not flow_element or flow_controls:
|
||||
from_selected_objects = True
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
|
||||
if element.is_a("IfcDistributionControlElement") and self.flow_control == 0:
|
||||
flow_controls.append(element)
|
||||
elif element.is_a("IfcDistributionFlowElement") and self.flow_element == 0:
|
||||
if flow_element:
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
"More than one flow element selected. Control can be assigned to only 1 flow element.",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
flow_element = element
|
||||
|
||||
if not flow_element:
|
||||
self.report({"ERROR"}, "No flow element selected.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
if not flow_controls:
|
||||
self.report({"ERROR"}, "No flow controls selected.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
for control in flow_controls:
|
||||
if self.assign:
|
||||
tool.Ifc.run(
|
||||
"system.assign_flow_control", relating_flow_element=flow_element, related_flow_control=control
|
||||
)
|
||||
else:
|
||||
tool.Ifc.run(
|
||||
"system.unassign_flow_control", relating_flow_element=flow_element, related_flow_control=control
|
||||
)
|
||||
|
||||
if from_selected_objects:
|
||||
self.report(
|
||||
{"INFO"}, f"{len(flow_controls)} flow controls were {'assigned' if self.assign else 'unassigned'}."
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -45,6 +45,11 @@ class System(PropertyGroup):
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
|
||||
class Zone(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
|
||||
def toggle_decorations(self, context):
|
||||
toggle = self.should_draw_decorations
|
||||
if toggle:
|
||||
@@ -60,7 +65,16 @@ class BIMSystemProperties(PropertyGroup):
|
||||
systems: CollectionProperty(name="Systems", type=System)
|
||||
active_system_index: IntProperty(name="Active System Index")
|
||||
active_system_id: IntProperty(name="Active System Id")
|
||||
edited_system_id: IntProperty(name="Edited System Id")
|
||||
system_class: EnumProperty(items=get_system_class, name="Class")
|
||||
should_draw_decorations: BoolProperty(
|
||||
name="Should Draw Decorations", description="Toggle system decorations", update=toggle_decorations
|
||||
)
|
||||
|
||||
|
||||
class BIMZoneProperties(PropertyGroup):
|
||||
attributes: CollectionProperty(name="Attributes", type=Attribute)
|
||||
is_loaded: BoolProperty(name="Is Loaded", default=False)
|
||||
is_editing: IntProperty(name="Is Editing", default=0)
|
||||
zones: CollectionProperty(name="Zones", type=Zone)
|
||||
active_zone_index: IntProperty(name="Active Zone Index")
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
import bpy
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.system.data import SystemData, ObjectSystemData, PortData
|
||||
from blenderbim.bim.module.system.data import SystemData, ZonesData, ActiveObjectZonesData, ObjectSystemData, PortData
|
||||
|
||||
|
||||
FLOW_DIRECTION_TO_ICON = {
|
||||
@@ -30,6 +31,15 @@ FLOW_DIRECTION_TO_ICON = {
|
||||
"NOTDEFINED": "CHECKBOX_DEHLT",
|
||||
}
|
||||
|
||||
SYSTEM_ICONS = {
|
||||
"IfcSystem": "EXTERNAL_DRIVE",
|
||||
"IfcDistributionSystem": "NETWORK_DRIVE",
|
||||
"IfcDistributionCircuit": "DRIVER",
|
||||
"IfcBuildingSystem": "MOD_BUILD",
|
||||
"IfcBuiltSystem": "MOD_BUILD",
|
||||
"IfcZone": "CUBE",
|
||||
}
|
||||
|
||||
|
||||
class BIM_PT_systems(Panel):
|
||||
bl_label = "Systems"
|
||||
@@ -42,15 +52,45 @@ class BIM_PT_systems(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not SystemData.is_loaded:
|
||||
SystemData.load()
|
||||
if not ObjectSystemData.is_loaded:
|
||||
ObjectSystemData.load()
|
||||
|
||||
def draw_system_ui(row, system_id, system_name, system_class):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=system_name, icon=SYSTEM_ICONS[system_class])
|
||||
op = row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.system = system_id
|
||||
op = row.operator("bim.unassign_system", text="", icon="X")
|
||||
op.system = system_id
|
||||
|
||||
self.props = context.scene.BIMSystemProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "should_draw_decorations")
|
||||
|
||||
row = self.layout.row()
|
||||
if active_system := tool.System.get_active_system():
|
||||
row.label(text=f"Active system:")
|
||||
row = self.layout.row(align=True)
|
||||
draw_system_ui(row, active_system.id(), active_system.Name, active_system.is_a())
|
||||
else:
|
||||
row.label(text="No active system is selected")
|
||||
|
||||
if ObjectSystemData.data["systems"]:
|
||||
row = self.layout.row()
|
||||
row.label(text="Active object systems:")
|
||||
for system in ObjectSystemData.data["systems"]:
|
||||
row = self.layout.row(align=True)
|
||||
draw_system_ui(row, system["id"], system["name"], system["ifc_class"])
|
||||
else:
|
||||
self.layout.label(text="No System associated with active object")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Systems Found".format(SystemData.data["total_systems"]), icon="OUTLINER")
|
||||
row.label(text="{} Systems Found in Project".format(SystemData.data["total_systems"]), icon="OUTLINER")
|
||||
if self.props.is_editing:
|
||||
row.operator("bim.disable_system_editing_ui", text="", icon="CANCEL")
|
||||
|
||||
@@ -58,7 +98,7 @@ class BIM_PT_systems(Panel):
|
||||
prop_with_search(row, self.props, "system_class", text="")
|
||||
row.operator("bim.add_system", text="", icon="ADD")
|
||||
else:
|
||||
row.operator("bim.load_systems", text="", icon="GREASEPENCIL")
|
||||
row.operator("bim.load_systems", text="", icon="IMPORT")
|
||||
|
||||
if self.props.is_editing:
|
||||
self.layout.template_list(
|
||||
@@ -70,7 +110,7 @@ class BIM_PT_systems(Panel):
|
||||
"active_system_index",
|
||||
)
|
||||
|
||||
if self.props.active_system_id:
|
||||
if self.props.edited_system_id:
|
||||
self.draw_editable_ui(context)
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
@@ -81,67 +121,6 @@ class BIM_PT_systems(Panel):
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
|
||||
class BIM_PT_object_systems(Panel):
|
||||
bl_label = "Systems"
|
||||
bl_idname = "BIM_PT_object_systems"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
bl_order = 1
|
||||
bl_parent_id = "BIM_PT_tab_services_object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
return False
|
||||
return IfcStore.get_file() and context.active_object.BIMObjectProperties.ifc_definition_id
|
||||
|
||||
def draw(self, context):
|
||||
if not ObjectSystemData.is_loaded:
|
||||
ObjectSystemData.load()
|
||||
self.props = context.scene.BIMSystemProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "should_draw_decorations")
|
||||
|
||||
if self.props.is_editing:
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.disable_system_editing_ui", text="", icon="CANCEL")
|
||||
self.layout.template_list(
|
||||
"BIM_UL_object_systems",
|
||||
"",
|
||||
self.props,
|
||||
"systems",
|
||||
self.props,
|
||||
"active_system_index",
|
||||
)
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{ObjectSystemData.data['total_systems']} Systems in IFC Project", icon="OUTLINER")
|
||||
row.operator("bim.load_systems", text="", icon="GREASEPENCIL")
|
||||
|
||||
system_icons = {
|
||||
"IfcSystem": "EXTERNAL_DRIVE",
|
||||
"IfcDistributionSystem": "NETWORK_DRIVE",
|
||||
"IfcDistributionCircuit": "DRIVER",
|
||||
"IfcBuildingSystem": "MOD_BUILD",
|
||||
"IfcBuiltSystem": "MOD_BUILD",
|
||||
"IfcZone": "CUBE",
|
||||
}
|
||||
for system in ObjectSystemData.data["systems"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=system["name"], icon=system_icons[system["ifc_class"]])
|
||||
op = row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.system = system["id"]
|
||||
op = row.operator("bim.unassign_system", text="", icon="X")
|
||||
op.system = system["id"]
|
||||
|
||||
if not ObjectSystemData.data["systems"]:
|
||||
self.layout.label(text="No System associated with Active Object")
|
||||
|
||||
|
||||
class BIM_PT_ports(Panel):
|
||||
bl_label = "Ports"
|
||||
bl_idname = "BIM_PT_ports"
|
||||
@@ -198,23 +177,24 @@ class BIM_PT_ports(Panel):
|
||||
cols = [row.column(align=True) for i in range(6)]
|
||||
|
||||
for i, port_data in enumerate(PortData.data["located_ports_data"]):
|
||||
port, port_obj, connected_obj = port_data
|
||||
port, port_obj_name, connected_obj_name = port_data
|
||||
flow_direction_icon = FLOW_DIRECTION_TO_ICON[port.FlowDirection or "NOTDEFINED"]
|
||||
if port_obj:
|
||||
if port_obj_name:
|
||||
cols[0].label(text="", icon=flow_direction_icon)
|
||||
cols[1].operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = port.id()
|
||||
cols[2].label(text=port_obj.name)
|
||||
cols[2].label(text=port_obj_name)
|
||||
else:
|
||||
cols[0].label(text="", icon=flow_direction_icon)
|
||||
cols[1].label(text="", icon="HIDE_ON")
|
||||
cols[2].label(text="Port is hidden")
|
||||
|
||||
if connected_obj:
|
||||
if connected_obj_name:
|
||||
connected_obj = bpy.data.objects[connected_obj_name]
|
||||
cols[3].operator("bim.disconnect_port", text="", icon="UNLINKED").element_id = port.id()
|
||||
cols[4].operator(
|
||||
"bim.select_entity", text="", icon="RESTRICT_SELECT_OFF"
|
||||
).ifc_id = connected_obj.BIMObjectProperties.ifc_definition_id
|
||||
cols[5].label(text=f"{connected_obj.name}")
|
||||
cols[5].label(text=connected_obj_name)
|
||||
else:
|
||||
cols[3].label(text="", icon="UNLINKED")
|
||||
cols[4].label(text="", icon="BLANK1")
|
||||
@@ -264,19 +244,21 @@ class BIM_PT_port(Panel):
|
||||
|
||||
# port located on
|
||||
row = layout.row(align=True)
|
||||
relating_object = PortData.data["port_relating_object"]
|
||||
relating_object_name = PortData.data["port_relating_object_name"]
|
||||
relating_object = bpy.data.objects[relating_object_name]
|
||||
row.label(text="Port located on:")
|
||||
row.label(text=relating_object.name)
|
||||
row.label(text=relating_object_name)
|
||||
row.operator(
|
||||
"bim.select_entity", text="", icon="RESTRICT_SELECT_OFF"
|
||||
).ifc_id = relating_object.BIMObjectProperties.ifc_definition_id
|
||||
|
||||
# object connected to the port
|
||||
row = layout.row(align=True)
|
||||
connected_object = PortData.data["port_connected_object"]
|
||||
if connected_object:
|
||||
connected_object_name = PortData.data["port_connected_object_name"]
|
||||
if connected_object_name:
|
||||
connected_object = bpy.data.objects[connected_object_name]
|
||||
row.label(text="Port connected to:")
|
||||
row.label(text=connected_object.name)
|
||||
row.label(text=connected_object_name)
|
||||
row.operator(
|
||||
"bim.select_entity", text="", icon="RESTRICT_SELECT_OFF"
|
||||
).ifc_id = connected_object.BIMObjectProperties.ifc_definition_id
|
||||
@@ -294,26 +276,152 @@ class BIM_PT_port(Panel):
|
||||
).direction = flow_direction
|
||||
|
||||
|
||||
class BIM_PT_flow_controls(Panel):
|
||||
bl_label = "Flow Controls"
|
||||
bl_idname = "BIM_PT_flow_controls"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
bl_order = 1
|
||||
bl_parent_id = "BIM_PT_tab_services_object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
return False
|
||||
element = tool.Ifc.get_entity(context.active_object)
|
||||
if not element or not (
|
||||
element.is_a("IfcDistributionControlElement") or element.is_a("IfcDistributionFlowElement")
|
||||
):
|
||||
return False
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
if not ObjectSystemData.is_loaded:
|
||||
ObjectSystemData.load()
|
||||
|
||||
def display_element(control_id, flow_element_id, displayed_object_name):
|
||||
displayed_object = bpy.data.objects[displayed_object_name]
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.assign_unassign_flow_control", text="", icon="X")
|
||||
op.flow_control = control_id
|
||||
op.flow_element = flow_element_id
|
||||
op.assign = False
|
||||
row.operator(
|
||||
"bim.select_entity", text="", icon="RESTRICT_SELECT_OFF"
|
||||
).ifc_id = displayed_object.BIMObjectProperties.ifc_definition_id
|
||||
row.label(text=f"{displayed_object_name}")
|
||||
|
||||
element = tool.Ifc.get_entity(context.active_object)
|
||||
flow_controls_data = ObjectSystemData.data["flow_controls_data"]
|
||||
if flow_controls_data["type"] == "IfcDistributionFlowElement":
|
||||
controls = flow_controls_data["controls"]
|
||||
row = self.layout.row(align=True)
|
||||
if controls:
|
||||
row.label(text="Controls assigned to the element:")
|
||||
else:
|
||||
row.label(text="No controls assigned to the flow element")
|
||||
row.operator("bim.assign_unassign_flow_control", text="", icon="ADD").assign = True
|
||||
if controls:
|
||||
for control_data in controls:
|
||||
control, control_obj_name = control_data
|
||||
display_element(control.id(), element.id(), control_obj_name)
|
||||
else:
|
||||
flow_element, flow_element_obj_name = flow_controls_data["flow_element"]
|
||||
row = self.layout.row(align=True)
|
||||
if flow_element:
|
||||
row.label(text="Flow element controlled by the flow control:")
|
||||
display_element(element.id(), flow_element.id(), flow_element_obj_name)
|
||||
else:
|
||||
row.label(text="No flow element controlled by the flow control")
|
||||
row.operator("bim.assign_unassign_flow_control", text="", icon="ADD").assign = True
|
||||
|
||||
|
||||
class BIM_PT_zones(Panel):
|
||||
bl_label = "Zones"
|
||||
bl_idname = "BIM_PT_zones"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_zones"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not ZonesData.is_loaded:
|
||||
ZonesData.load()
|
||||
self.props = context.scene.BIMZoneProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Zones Found".format(ZonesData.data["total_zones"]), icon="SEQ_STRIP_META")
|
||||
if not self.props.is_loaded:
|
||||
row.operator("bim.load_zones", text="", icon="IMPORT")
|
||||
return
|
||||
|
||||
row.operator("bim.add_zone", text="", icon="ADD")
|
||||
row.operator("bim.unload_zones", text="", icon="CANCEL")
|
||||
|
||||
if self.props.zones and self.props.active_zone_index < len(self.props.zones):
|
||||
row = self.layout.row(align=True)
|
||||
ifc_definition_id = self.props.zones[self.props.active_zone_index].ifc_definition_id
|
||||
row.operator("bim.enable_editing_zone", text="Edit Zone", icon="GREASEPENCIL").zone = ifc_definition_id
|
||||
row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF").system = ifc_definition_id
|
||||
row.operator("bim.assign_system", text="", icon="KEYFRAME_HLT").system = ifc_definition_id
|
||||
row.operator("bim.unassign_system", text="", icon="KEYFRAME").system = ifc_definition_id
|
||||
row.operator("bim.remove_zone", text="", icon="X").zone = ifc_definition_id
|
||||
|
||||
self.layout.template_list("BIM_UL_zones", "", self.props, "zones", self.props, "active_zone_index")
|
||||
|
||||
if self.props.is_editing:
|
||||
blenderbim.bim.helper.draw_attributes(self.props.attributes, self.layout)
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_zone", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_zone", icon="CANCEL", text="")
|
||||
|
||||
|
||||
class BIM_PT_active_object_zones(Panel):
|
||||
bl_label = "Active Object Zones"
|
||||
bl_idname = "BIM_PT_active_object_zones"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_zones"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Ifc.get() and context.active_object and tool.Ifc.get_entity(context.active_object)
|
||||
|
||||
def draw(self, context):
|
||||
if not ActiveObjectZonesData.is_loaded:
|
||||
ActiveObjectZonesData.load()
|
||||
self.props = context.scene.BIMZoneProperties
|
||||
|
||||
for zone in ActiveObjectZonesData.data["zones"]:
|
||||
row = self.layout.row()
|
||||
row.label(text=zone, icon="SEQ_STRIP_META")
|
||||
|
||||
if not ActiveObjectZonesData.data["zones"]:
|
||||
row = self.layout.row()
|
||||
row.label(text="Active Object Has No Zones")
|
||||
|
||||
|
||||
class BIM_UL_systems(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
system_icons = {
|
||||
"IfcSystem": "EXTERNAL_DRIVE",
|
||||
"IfcDistributionSystem": "NETWORK_DRIVE",
|
||||
"IfcDistributionCircuit": "DRIVER",
|
||||
"IfcBuildingSystem": "MOD_BUILD",
|
||||
"IfcBuiltSystem": "MOD_BUILD",
|
||||
"IfcZone": "CUBE",
|
||||
}
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.name, icon=system_icons[item.ifc_class])
|
||||
row.label(text=item.name, icon=SYSTEM_ICONS[item.ifc_class])
|
||||
system_id = item.ifc_definition_id
|
||||
if context.scene.BIMSystemProperties.active_system_id == system_id:
|
||||
row.operator("bim.assign_system", text="", icon="ADD").system = item.ifc_definition_id
|
||||
if context.scene.BIMSystemProperties.edited_system_id == system_id:
|
||||
op = row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.system = system_id
|
||||
row.operator("bim.edit_system", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_system", text="", icon="CANCEL")
|
||||
elif context.scene.BIMSystemProperties.active_system_id:
|
||||
elif context.scene.BIMSystemProperties.edited_system_id:
|
||||
op = row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.system = system_id
|
||||
op = row.operator("bim.remove_system", text="", icon="X")
|
||||
@@ -327,17 +435,8 @@ class BIM_UL_systems(UIList):
|
||||
op.system = system_id
|
||||
|
||||
|
||||
class BIM_UL_object_systems(UIList):
|
||||
class BIM_UL_zones(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
system_icons = {
|
||||
"IfcSystem": "EXTERNAL_DRIVE",
|
||||
"IfcDistributionSystem": "NETWORK_DRIVE",
|
||||
"IfcDistributionCircuit": "DRIVER",
|
||||
"IfcBuildingSystem": "MOD_BUILD",
|
||||
"IfcBuiltSystem": "MOD_BUILD",
|
||||
"IfcZone": "CUBE",
|
||||
}
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.name, icon=system_icons[item.ifc_class])
|
||||
row.operator("bim.assign_system", text="", icon="ADD").system = item.ifc_definition_id
|
||||
row.label(text=item.name)
|
||||
|
||||
@@ -410,7 +410,7 @@ class AddType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj=obj,
|
||||
predefined_type=predefined_type,
|
||||
ifc_class="IfcStairFlightType",
|
||||
should_add_representation=True,
|
||||
should_add_representation=False,
|
||||
context=body,
|
||||
)
|
||||
tool.Blender.select_and_activate_single_object(context, obj)
|
||||
|
||||
@@ -30,7 +30,7 @@ class BIM_PT_units(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_geometry"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -25,6 +25,7 @@ classes = (
|
||||
operator.RemoveFilling,
|
||||
operator.RemoveOpening,
|
||||
operator.SelectDecomposition,
|
||||
operator.BooleansMarkAsManual,
|
||||
prop.VoidProperties,
|
||||
ui.BIM_PT_voids,
|
||||
ui.BIM_PT_booleans,
|
||||
|
||||
@@ -67,32 +67,39 @@ class VoidsData:
|
||||
return results
|
||||
|
||||
|
||||
|
||||
class BooleansData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {"total_booleans": cls.total_booleans()}
|
||||
cls.data = {}
|
||||
cls.data["total_booleans"] = cls.booleans()
|
||||
cls.data["manual_booleans"] = cls.manual_booleans()
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def total_booleans(cls):
|
||||
def booleans(cls):
|
||||
obj = bpy.context.active_object
|
||||
if (
|
||||
not obj.data
|
||||
or not hasattr(obj.data, "BIMMeshProperties")
|
||||
or not obj.data.BIMMeshProperties.ifc_definition_id
|
||||
):
|
||||
return 0
|
||||
return []
|
||||
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
total_booleans = 0
|
||||
for item in representation.Items:
|
||||
while True:
|
||||
if item.is_a("IfcBooleanResult"):
|
||||
total_booleans += 1
|
||||
item = item.FirstOperand
|
||||
else:
|
||||
break
|
||||
return total_booleans
|
||||
return tool.Model.get_booleans(representation=representation)
|
||||
|
||||
@classmethod
|
||||
def manual_booleans(cls):
|
||||
obj = bpy.context.active_object
|
||||
if (
|
||||
not obj.data
|
||||
or not hasattr(obj.data, "BIMMeshProperties")
|
||||
or not obj.data.BIMMeshProperties.ifc_definition_id
|
||||
):
|
||||
return []
|
||||
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
return tool.Model.get_manual_booleans(tool.Ifc.get_entity(obj), representation=representation)
|
||||
|
||||
@@ -137,7 +137,7 @@ class RemoveOpening(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
if opening_obj:
|
||||
opening_obj.name = "/".join(opening_obj.name.split("/")[1:])
|
||||
IfcStore.unlink_element(obj=opening_obj)
|
||||
tool.Ifc.unlink(obj=opening_obj)
|
||||
|
||||
ifcopenshell.api.run("void.remove_opening", tool.Ifc.get(), opening=opening)
|
||||
|
||||
@@ -218,3 +218,42 @@ class SelectDecomposition(bpy.types.Operator):
|
||||
if subobj:
|
||||
subobj.select_set(True)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class BooleansMarkAsManual(bpy.types.Operator):
|
||||
bl_idname = "bim.booleans_mark_as_manual"
|
||||
bl_label = "Mark Booleans as Manual"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = (
|
||||
"\nMark all booleans in object's current representations as manual.\n"
|
||||
"Manual booleans will be preserved until they are removed explicitly"
|
||||
)
|
||||
mark_as_manual: bpy.props.BoolProperty(name="Mark as Manual", default=True)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj = context.active_object
|
||||
if (
|
||||
obj
|
||||
and tool.Ifc.get_entity(obj)
|
||||
and hasattr(obj.data, "BIMMeshProperties")
|
||||
and obj.data.BIMMeshProperties.ifc_definition_id
|
||||
):
|
||||
return True
|
||||
cls.poll_message_set("Need to select IFC element with representation")
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
booleans = tool.Model.get_booleans(representation=representation)
|
||||
|
||||
if self.mark_as_manual:
|
||||
tool.Model.mark_manual_booleans(element, booleans)
|
||||
else:
|
||||
tool.Model.unmark_manual_booleans(element, booleans)
|
||||
|
||||
self.report({"INFO"}, f"{len(booleans)} were marked as {'manual' if self.mark_as_manual else 'automatic'}")
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -111,13 +111,21 @@ class BIM_PT_booleans(Panel):
|
||||
|
||||
if context.active_object.data.BIMMeshProperties.ifc_definition_id:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"{BooleansData.data['total_booleans']} Booleans Found")
|
||||
row.operator("bim.add_boolean", text="Apply Boolean", icon="ADD")
|
||||
total_booleans = BooleansData.data["total_booleans"]
|
||||
manual_booleans = BooleansData.data["manual_booleans"]
|
||||
row.label(text=f"{len(total_booleans)} Booleans Found ({len(manual_booleans)} Manual)")
|
||||
row.operator("bim.add_boolean", text="", icon="ADD")
|
||||
show_boolean_button = row.row(align=True)
|
||||
show_boolean_button.operator("bim.show_booleans", text="", icon="HIDE_OFF")
|
||||
show_boolean_button.enabled = BooleansData.data["total_booleans"] > 0
|
||||
show_boolean_button.enabled = len(total_booleans) > 0
|
||||
row.operator("bim.hide_booleans", text="", icon="HIDE_ON")
|
||||
|
||||
booleans_are_manual = len(manual_booleans) == len(total_booleans)
|
||||
op = row.operator(
|
||||
"bim.booleans_mark_as_manual", text="", icon="PINNED" if booleans_are_manual else "UNPINNED"
|
||||
)
|
||||
op.mark_as_manual = not booleans_are_manual
|
||||
|
||||
elif context.active_object.data.BIMMeshProperties.ifc_boolean_id:
|
||||
upsteam_obj = context.active_object.data.BIMMeshProperties.obj
|
||||
upstream_obj_ifc_id = upsteam_obj.BIMObjectProperties.ifc_definition_id
|
||||
|
||||
@@ -816,6 +816,10 @@ def update_enum_property_search_prop(self, context):
|
||||
if prop.name == self.dummy_name:
|
||||
setattr(context.data, self.prop_name, self.collection_identifiers[i].name)
|
||||
predefined_type = self.collection_predefined_types[i].name
|
||||
if self.first_launch:
|
||||
self.first_launch = False
|
||||
else:
|
||||
context.window.screen = context.window.screen
|
||||
if predefined_type:
|
||||
try:
|
||||
setattr(context.data, "ifc_predefined_type", predefined_type)
|
||||
@@ -828,6 +832,7 @@ class BIM_OT_enum_property_search(bpy.types.Operator):
|
||||
bl_idname = "bim.enum_property_search"
|
||||
bl_label = "Search For Property"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
first_launch: bpy.props.BoolProperty(default=True, options={"SKIP_SAVE"})
|
||||
dummy_name: bpy.props.StringProperty(name="Property", update=update_enum_property_search_prop)
|
||||
collection_names: bpy.props.CollectionProperty(type=StrProperty)
|
||||
collection_identifiers: bpy.props.CollectionProperty(type=StrProperty)
|
||||
|
||||
@@ -174,6 +174,18 @@ def update_attribute_value(self, context):
|
||||
self.is_null = False
|
||||
|
||||
|
||||
def update_is_null(self, context):
|
||||
if not self.is_null:
|
||||
return
|
||||
self.string_value = ""
|
||||
self.int_value = 0
|
||||
self.float_value = 0
|
||||
self.length_value = 0
|
||||
self.bool_value = False
|
||||
if self.is_null is not True:
|
||||
self.is_null = True
|
||||
|
||||
|
||||
def set_int_value(self, new_value):
|
||||
set_numerical_value(self, "int_value", new_value)
|
||||
|
||||
@@ -240,7 +252,7 @@ class Attribute(PropertyGroup):
|
||||
enum_items: StringProperty(name="Value")
|
||||
enum_descriptions: CollectionProperty(type=StrProperty)
|
||||
enum_value: EnumProperty(items=get_attribute_enum_values, name="Value", update=update_attribute_value)
|
||||
is_null: BoolProperty(name="Is Null")
|
||||
is_null: BoolProperty(name="Is Null", update=update_is_null)
|
||||
is_optional: BoolProperty(name="Is Optional")
|
||||
is_uri: BoolProperty(name="Is Uri", default=False)
|
||||
is_selected: BoolProperty(name="Is Selected", default=False)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -132,6 +132,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
description='E.g. [["inkscape", "svg", "-o", "eps"], ["pstoedit", "-dt", "-f", "dxf:-polyaslines -mm", "eps", "dxf", "-psarg", "-dNOSAFER"]]',
|
||||
)
|
||||
svg_command: StringProperty(name="SVG Command", description='E.g. [["firefox", "path"]]')
|
||||
layout_svg_command: StringProperty(name="Layout SVG Command", description='E.g. [["firefox", "path"]]')
|
||||
pdf_command: StringProperty(name="PDF Command", description='E.g. [["firefox", "path"]]')
|
||||
spreadsheet_command: StringProperty(name="Spreadsheet Command", description='E.g. [["libreoffice", "path"]]')
|
||||
openlca_port: IntProperty(name="OpenLCA IPC Port", default=8080)
|
||||
@@ -205,6 +206,8 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
row = layout.row()
|
||||
row.prop(self, "svg_command")
|
||||
row = layout.row()
|
||||
row.prop(self, "layout_svg_command")
|
||||
row = layout.row()
|
||||
row.prop(self, "pdf_command")
|
||||
row = layout.row()
|
||||
row.prop(self, "spreadsheet_command")
|
||||
@@ -336,7 +339,7 @@ class BIM_PT_tabs(Panel):
|
||||
tab_entry.enabled = enabled
|
||||
|
||||
|
||||
class BIM_PT_project_info(Panel):
|
||||
class BIM_PT_tab_project_info(Panel):
|
||||
bl_label = "Project Info"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
@@ -350,16 +353,30 @@ class BIM_PT_project_info(Panel):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_project_setup(Panel):
|
||||
class BIM_PT_tab_project_setup(Panel):
|
||||
bl_label = "Project Setup"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "PROJECT")
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_stakeholders(Panel):
|
||||
bl_label = "Stakeholders"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "PROJECT")
|
||||
return tool.Blender.is_tab(context, "PROJECT") and tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
@@ -394,7 +411,7 @@ class BIM_PT_tab_grouping_and_filtering(Panel):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_geometry(Panel):
|
||||
class BIM_PT_tab_geometry(Panel):
|
||||
bl_label = "Geometry"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
@@ -492,6 +509,20 @@ class BIM_PT_tab_services(Panel):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_zones(Panel):
|
||||
bl_label = "Zones"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SERVICES") and tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_quality_control(Panel):
|
||||
bl_label = "Quality Control"
|
||||
bl_space_type = "PROPERTIES"
|
||||
@@ -644,6 +675,21 @@ class BIM_PT_tab_styles(Panel):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_profiles(Panel):
|
||||
bl_label = "Profiles"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_order = 1
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "GEOMETRY") and tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_sheets(Panel):
|
||||
bl_label = "Sheets"
|
||||
bl_space_type = "PROPERTIES"
|
||||
|
||||
@@ -44,13 +44,6 @@ def add_instance_flooring_covering_from_cursor(ifc, spatial, model, Type, geomet
|
||||
name = "Covering"
|
||||
mesh = spatial.get_named_mesh_from_bmesh(name = name, bmesh = bm)
|
||||
|
||||
|
||||
# spatial.edit_active_space_obj_from_mesh(mesh)
|
||||
# else:
|
||||
# if relating_type:
|
||||
# name = model.generate_occurrence_name(relating_type, "IfcCovering")
|
||||
# else:
|
||||
|
||||
obj = spatial.get_named_obj_from_mesh(name, mesh)
|
||||
|
||||
spatial.set_obj_origin_to_cursor_position(obj)
|
||||
@@ -63,7 +56,49 @@ def add_instance_flooring_covering_from_cursor(ifc, spatial, model, Type, geomet
|
||||
body = spatial.get_body_representation(obj)
|
||||
spatial.regen_obj_representation(ifc, geometry, obj, body)
|
||||
|
||||
def regen_selected_flooring_object(ifc, spatial, model, Type, geometry):
|
||||
def add_instance_ceiling_covering_from_cursor(ifc, spatial, model, Type, geometry, covering):
|
||||
active_obj = spatial.get_active_obj()
|
||||
selected_objects = spatial.get_selected_objects()
|
||||
element = None
|
||||
relating_type_id = spatial.get_relating_type_id()
|
||||
|
||||
relating_type = None
|
||||
if relating_type_id:
|
||||
relating_type = ifc.get().by_id(int(relating_type_id))
|
||||
if not relating_type.is_a("IfcCoveringType"):
|
||||
relating_type = None
|
||||
|
||||
if selected_objects and active_obj:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_active_obj(active_obj)
|
||||
element = ifc.get_entity(active_obj)
|
||||
|
||||
else:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_cursor()
|
||||
z = covering.get_z_from_ceiling_height()
|
||||
|
||||
space_polygon = spatial.get_space_polygon_from_context_visible_objects(x, y)
|
||||
|
||||
if not space_polygon:
|
||||
return
|
||||
|
||||
bm = spatial.get_bmesh_from_polygon(space_polygon, h=0)
|
||||
name = "Covering"
|
||||
mesh = spatial.get_named_mesh_from_bmesh(name = name, bmesh = bm)
|
||||
|
||||
obj = spatial.get_named_obj_from_mesh(name, mesh)
|
||||
|
||||
spatial.set_obj_origin_to_cursor_position(obj)
|
||||
spatial.traslate_obj_to_z_location(obj, z)
|
||||
spatial.link_obj_to_active_collection(obj)
|
||||
points = spatial.get_2d_vertices_from_obj(obj)
|
||||
spatial.assign_type_to_obj(obj)
|
||||
|
||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices = points)
|
||||
body = spatial.get_body_representation(obj)
|
||||
spatial.regen_obj_representation(ifc, geometry, obj, body)
|
||||
|
||||
|
||||
def regen_selected_covering_object(ifc, spatial, model, Type, geometry):
|
||||
active_obj = spatial.get_active_obj()
|
||||
selected_objects = spatial.get_selected_objects()
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ def add_sheet(ifc, drawing, titleblock=None):
|
||||
|
||||
|
||||
def open_sheet(drawing, sheet=None):
|
||||
drawing.open_svg(drawing.get_document_uri(sheet, "LAYOUT"))
|
||||
drawing.open_layout_svg(drawing.get_document_uri(sheet, "LAYOUT"))
|
||||
|
||||
|
||||
def remove_sheet(ifc, drawing, sheet=None):
|
||||
|
||||
@@ -104,11 +104,15 @@ def switch_representation(
|
||||
return
|
||||
|
||||
entity = ifc.get_entity(obj)
|
||||
current_obj_data = geometry.get_object_data(obj)
|
||||
|
||||
# doesn't resolve mapped representations in case if it's going to have openings
|
||||
# otherwise we would also add openings to the type and other occurences mesh data
|
||||
has_openings = apply_openings and getattr(entity, "HasOpenings", None)
|
||||
if not has_openings:
|
||||
if has_openings:
|
||||
# if it has openings make sure to switch to element's mapped representation
|
||||
representation = geometry.unresolve_type_representation(representation, entity)
|
||||
else:
|
||||
# doesn't resolve mapped representations in case if it's going to have openings
|
||||
# otherwise we would also add openings to the type and other occurences mesh data
|
||||
representation = geometry.resolve_mapped_representation(representation)
|
||||
|
||||
old_repr_data = geometry.get_representation_data(representation)
|
||||
@@ -125,6 +129,10 @@ def switch_representation(
|
||||
# we assume that all the occurences and the type have the same representation context active
|
||||
# so geometry.delete_data cannot remove the data that's still used by some other object
|
||||
if should_reload and old_repr_data:
|
||||
# if current object was using some temporary mesh (like during profile edit mode) instead of `old_repr_data`
|
||||
# then `change_object_data` won't switch the mesh for all the occurences and we need to do it explicitly
|
||||
if current_obj_data != old_repr_data and geometry.has_data_users(old_repr_data):
|
||||
geometry.replace_object_data_globally(old_repr_data, new_repr_data)
|
||||
geometry.delete_data(old_repr_data)
|
||||
|
||||
geometry.clear_modifiers(obj)
|
||||
|
||||
@@ -21,9 +21,9 @@ def unlink_material(ifc, obj=None):
|
||||
ifc.unlink(obj=obj)
|
||||
|
||||
|
||||
def add_material(ifc, material, style, obj=None):
|
||||
def add_material(ifc, material, style, obj=None, name=None):
|
||||
if not obj:
|
||||
obj = material.add_default_material_object()
|
||||
obj = material.add_default_material_object(name)
|
||||
ifc_material = ifc.run("material.add_material", name=material.get_name(obj))
|
||||
ifc.link(ifc_material, obj)
|
||||
ifc_style = style.get_style(obj)
|
||||
|
||||
@@ -47,7 +47,7 @@ def remove_system(ifc, system_tool, system=None):
|
||||
|
||||
def enable_editing_system(system_tool, system=None):
|
||||
system_tool.import_system_attributes(system)
|
||||
system_tool.set_active_system(system)
|
||||
system_tool.set_active_edited_system(system)
|
||||
|
||||
|
||||
def disable_editing_system(system):
|
||||
@@ -64,6 +64,7 @@ def unassign_system(ifc, system=None, product=None):
|
||||
|
||||
def select_system_products(system_tool, system=None):
|
||||
system_tool.select_system_products(system)
|
||||
system_tool.set_active_system(system)
|
||||
|
||||
|
||||
def show_ports(ifc, system, spatial, element=None):
|
||||
|
||||
@@ -316,6 +316,7 @@ class Drawing:
|
||||
def is_drawing_active(cls): pass
|
||||
def is_editing_sheets(cls): pass
|
||||
def move_file(cls, src, dest): pass
|
||||
def open_layout_svg(cls, uri): pass
|
||||
def open_spreadsheet(cls, uri): pass
|
||||
def open_svg(cls, filepath): pass
|
||||
def remove_literal_from_annotation(cls, obj, literal): pass
|
||||
@@ -369,6 +370,7 @@ class Geometry:
|
||||
def remove_connection(cls, connection): pass
|
||||
def rename_object(cls, obj, name): pass
|
||||
def replace_object_with_empty(cls, obj): pass
|
||||
def replace_object_data_globally(cls, old_data, new_data): pass
|
||||
def resolve_mapped_representation(cls, representation): pass
|
||||
def run_geometry_update_representation(cls, obj=None): pass
|
||||
def run_style_add_style(cls, obj=None): pass
|
||||
@@ -377,6 +379,7 @@ class Geometry:
|
||||
def should_force_triangulation(cls): pass
|
||||
def should_generate_uvs(cls, obj): pass
|
||||
def should_use_presentation_style_assignment(cls): pass
|
||||
def unresolve_type_representation(cls, representation, element): pass
|
||||
|
||||
|
||||
@interface
|
||||
@@ -445,7 +448,7 @@ class Loader:
|
||||
|
||||
@interface
|
||||
class Material:
|
||||
def add_default_material_object(cls): pass
|
||||
def add_default_material_object(cls, name): pass
|
||||
def add_material_to_set(cls, material_set, material): pass
|
||||
def delete_object(cls, obj): pass
|
||||
def disable_editing_material(cls): pass
|
||||
@@ -848,6 +851,9 @@ class Spatial:
|
||||
def regen_obj_representation(cls, ifc, geometry, obj, body): pass
|
||||
def toggle_spaces_visibility_wired_and_textured(cls, spaces): pass
|
||||
|
||||
@interface
|
||||
class Covering:
|
||||
def get_z_from_ceiling_height(cls): pass
|
||||
|
||||
@interface
|
||||
class Structural:
|
||||
@@ -915,6 +921,7 @@ class System:
|
||||
def run_geometry_edit_object_placement(cls, obj=None): pass
|
||||
def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None): pass
|
||||
def select_system_products(cls, system): pass
|
||||
def set_active_edited_system(cls, system): pass
|
||||
def set_active_system(cls, system): pass
|
||||
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ from blenderbim.tool.resource import Resource
|
||||
from blenderbim.tool.root import Root
|
||||
from blenderbim.tool.sequence import Sequence
|
||||
from blenderbim.tool.spatial import Spatial
|
||||
from blenderbim.tool.covering import Covering
|
||||
from blenderbim.tool.structural import Structural
|
||||
from blenderbim.tool.style import Style
|
||||
from blenderbim.tool.surveyor import Surveyor
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user